1

Or at least that's what I believe is the problem.

I had to add a bipolar LED library to my sketch to get them to work.

As it is now I only have it activated by a specified bit so it can properly drive yellow. Green and Red were working fine (the flickering problem not withstanding) without the bipolar library.

If I have no sensors enabled in the software (java model railroad interface) I'm using with the sketch, then the bipolar LEDs work fine.

The second I add a sensor, then the arduino starts polling the inputs, at which time the bipolar LEDs (all colors) go all wonky. Their flickering coincides with the flickering of the tx/rx lights on the board, so I'm assuming that the polling of the sensors is what is causing it.

Note that the bipolars are the only LEDs attached to another output pin instead of the ground. It's almost like it's polling any pin that's low at the time, which we obviously don't need/want with the pins for the LEDs.

I even changed the order of the sketch which seemed to slightly effect the flickering, but not enough not to be annoying. Keep in mind my level of understanding programming this is at the level of being able to combine sketches...which conflict led us here.

#include <CMRI.h>
#include <CrossingFlasher.h>
#include <BiColorLED.h>

CMRI cmri; // defaults to a SMINI with address 0. SMINI = 24 inputs, 48 outputs
CrossingFlasher crossing(10, 11); // crossbucks
BiColorLED led=BiColorLED(46,47); // (pin 1, pin 2) 
BiColorLED led2=BiColorLED(48,49); // (pin 1, pin 2)
BiColorLED led3=BiColorLED(50,51); // (pin 1, pin 2)
BiColorLED led4=BiColorLED(52,53); // (pin 1, pin 2)

void setup() {
  Serial.begin(9600); // make sure this matches your speed set in JMRI
  for (int i=34; i<=53; i++) { pinMode(i, OUTPUT); }
  for (int i=22; i<=33; i++) { pinMode(i, INPUT); digitalWrite(i, HIGH); }
  for (int i=10; i<=11; i++) { pinMode(i, OUTPUT); }
}

void loop() {
  // 1: build up a packet
  cmri.process();

  // 4: update crossing lights. Reads bit 0
  if (cmri.get_bit(0) == true)
    crossing.on();
  else
    crossing.off();

  // 5: update crossing lights
  crossing.update();

  // 6: update Yellow lights. Reads bit 1
  if (cmri.get_bit(1) == true)
    led.setColor(3);
  else
    led.setColor(0);

  led.drive();

  // 7: update Yellow lights. Reads bit 2
  if (cmri.get_bit(2) == true)
    led2.setColor(3);
  else
    led2.setColor(0);

  led.drive();

  // 8: update Yellow lights. 
  if (cmri.get_bit(3) == true)
    led3.setColor(3);
  else
    led3.setColor(0);

  led.drive();

  // 9: update Yellow lights. 
  if (cmri.get_bit(4) == true)
    led4.setColor(3);
  else
    led4.setColor(0);

  led.drive();

  // 2: update outputs
  digitalWrite(34, cmri.get_bit(5));
  digitalWrite(35, cmri.get_bit(6));
  digitalWrite(36, cmri.get_bit(7));
  digitalWrite(37, cmri.get_bit(8));
  digitalWrite(38, cmri.get_bit(9));
  digitalWrite(39, cmri.get_bit(10));
  digitalWrite(40, cmri.get_bit(11));
  digitalWrite(41, cmri.get_bit(12));
  digitalWrite(42, cmri.get_bit(13));
  digitalWrite(43, cmri.get_bit(14));
  digitalWrite(44, cmri.get_bit(15));
  digitalWrite(45, cmri.get_bit(16));
  digitalWrite(46, cmri.get_bit(17));
  digitalWrite(47, cmri.get_bit(18));
  digitalWrite(48, cmri.get_bit(19));
  digitalWrite(49, cmri.get_bit(20));
  digitalWrite(50, cmri.get_bit(21));
  digitalWrite(51, cmri.get_bit(22));
  digitalWrite(52, cmri.get_bit(23));
  digitalWrite(53, cmri.get_bit(24));

  // 3: update inputs (invert digitalRead due to the pullups)
  cmri.set_bit(0, !digitalRead(22));
  cmri.set_bit(1, !digitalRead(23));
  cmri.set_bit(2, !digitalRead(24));
  cmri.set_bit(3, !digitalRead(25));
  cmri.set_bit(4, !digitalRead(26));
  cmri.set_bit(5, !digitalRead(27));
  cmri.set_bit(6, !digitalRead(28));
  cmri.set_bit(7, !digitalRead(29));
  cmri.set_bit(8, !digitalRead(30));
  cmri.set_bit(9, !digitalRead(31));
  cmri.set_bit(10, !digitalRead(32));
  cmri.set_bit(11, !digitalRead(33));
}

I tried all the suggestions and more, same results.

My uneducated guess is the problem is probably with the CMRI.h library. Because I FINALLY managed to actually get the native support for yellow in the software I was using (JMRI) working without any extra bipolar libraries, and guess what? STILL flickers, though this native JMRI support tends to make it more orange/red in color than the bipolar library did (I prefer the later).

The cmri library is actually attempting to make the arduino emulate another piece of hardware, so for all I know it might not even be possible to get it to act right.

I've included both cpp and h scripts. If no one actually figures this out, thanks anyway.

The CMRI.cpp file:

/*
CMRI - a small library for Arduino to interface with the C/MRI
computer control system for model railroads
Copyright (C) 2012 Michael Adams (www.michael.net.nz)
All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a 
copy of this software and associated documentation files (the "Software"), 
to deal in the Software without restriction, including without limitation 
the rights to use, copy, modify, merge, publish, distribute, sublicense, 
and/or sell copies of the Software, and to permit persons to whom the 
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included 
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.
*/

#include "CMRI.h"
#include <Arduino.h>

CMRI::CMRI(unsigned int address, unsigned int input_bits, unsigned int output_bits, Stream &serial_class)
    // store details
: _address(address)
, _rx_length((output_bits + 7) / 8)
, _tx_length((input_bits + 7) / 8)
, _serial(serial_class)

    // set state
, _rx_buffer((char *) malloc(_rx_length))
, _tx_buffer((char *) malloc(_tx_length))

    // parsing state
, _mode(PREAMBLE_1)
, _rx_index(0)

{
    // clear to zero
    for(int i=0; i<_rx_length; i++)
        _rx_buffer[i] = 0;
    for(int i=0; i<_tx_length; i++)
        _tx_buffer[i] = 0;
}

void CMRI::set_address(unsigned int address)
{
    _address = address;
}

// reads in serial data, decodes packets
// automatically responds to POLL requests
// returns packet type so if we got a SET request you know to update your outputs
bool CMRI::process()
{
    while (_serial.available() > 0)
    {
        if (process_char(_serial.read()))
        {
            return true;
        }
    }
    return false;
}

bool CMRI::process_char(char c)
{
    // if it's a SET that's fine do nothing
    // if it's an INIT that's also fine, we don't really care
    // if it's a GET, well, do nothing since it must be someone else replying
    // if it's a POLL then reply straight away with our data
    switch (_decode(c))
    {
        case POLL:
            transmit();
            return true;

        case SET:
            return true;

        default:
            return false;
    }
}



// public methods

bool CMRI::get_bit(int pos)
{
    // 1: divide index by 8 to get byte offset
    char c = get_byte(pos / 8);
    // 2: return bit at that location
    return (bool) ((c >> (pos % 8)) & 0x01);
}

char CMRI::get_byte(int pos)
{
    if (pos > _rx_length)
        return 0; // out of bounds
    else
        return _rx_buffer[pos];
}

bool CMRI::set_bit(int pos, bool bit)
{
    if ((pos + 7) / 8 > _tx_length)
        return false; // out of bounds
    else
    {
        int index = pos / 8;
        _tx_buffer[index] = bit
            ? _tx_buffer[index] | 1 << pos % 8      // if bit=1, then OR it
            : _tx_buffer[index] & ~(1 << pos % 8) // if bit=0, then NAND it
        ;
        return true;
    }
}

bool CMRI::set_byte(int pos, char b)
{
    if (pos > _tx_length)
        return false; // out of bounds
    else
    {
        _tx_buffer[pos] = b;
        return true;
    }
}


void CMRI::transmit()
{
    delay(50); // tiny delay to let things recover
    _serial.write(255);
    _serial.write(255);
    _serial.write(STX);
    _serial.write(65 + _address);
    _serial.write(GET);
    for (int i=0; i<_tx_length; i++)
    {
        if (_tx_buffer[i] == ETX)
            _serial.write(ESC); // escape because this looks like an STX bit (very basic protocol)
        if (_tx_buffer[i] == ESC)
            _serial.write(ESC); // escape because this looks like an escape bit (very basic protocol)
        _serial.write(_tx_buffer[i]);
    }
    _serial.write(ETX);
    _serial.flush();
}

// Private methods
uint8_t CMRI::_decode(uint8_t c)
{
    switch(_mode)
    {
        case PREAMBLE_1:
            _rx_index = 0;
            if (c == 0xFF)
                _mode = PREAMBLE_2;
            break;

        case PREAMBLE_2:
            if (c == 0xFF)
                _mode = PREAMBLE_3;
            else
                _mode = PREAMBLE_1;
            break;

        case PREAMBLE_3:
            if (c == STX)
                _mode = DECODE_ADDR;
            else
                _mode = PREAMBLE_1;
            break;

        case DECODE_ADDR:
            if (c == 'A' + _address)
                _mode = DECODE_CMD;
            else if (c >= 'A')
                _mode = IGNORE_CMD;
            else
                _mode = PREAMBLE_1;
            break;

        case DECODE_CMD:
            if (c == SET)
                _mode = DECODE_DATA;
            else if (c == POLL)
                goto POSTAMBLE_POLL;
            else
                _mode = POSTAMBLE_OTHER;
            break;

        case IGNORE_CMD:
            _mode = IGNORE_DATA;
            break;

        case DECODE_DATA:
            if (c == ESC)
                _mode = DECODE_ESC_DATA;
            else if (c == ETX)
                goto POSTAMBLE_SET;
            else if (_rx_index >= _rx_length)
                { }
            else
                _rx_buffer[_rx_index++] = c;
            break;

        case DECODE_ESC_DATA:
            if (_rx_index >= _rx_length)
                { }
            else
                _rx_buffer[_rx_index++] = c;
            _mode = DECODE_DATA;
            break;

        case IGNORE_DATA:
            if (c == ESC)
                _mode = IGNORE_ESC_DATA;
            else if (c == ETX)
                goto POSTAMBLE_IGNORE;
            break;

        case IGNORE_ESC_DATA:
            _mode = IGNORE_DATA;
            break;

        case POSTAMBLE_OTHER:
            _mode = PREAMBLE_1;
            break;
    }
    return NULL;

POSTAMBLE_SET:
        _mode = PREAMBLE_1;
        return SET;

POSTAMBLE_POLL:
        _mode = PREAMBLE_1;
        return POLL;

POSTAMBLE_IGNORE:
        _mode = PREAMBLE_1;
        return NULL;
}

The CMRI.h file:

    /*
    CMRI - a small library for Arduino to interface with the C/MRI
    computer control system for model railroads
    Copyright (C) 2012 Michael Adams (www.michael.net.nz)
    All rights reserved.

    Permission is hereby granted, free of charge, to any person obtaining a 
    copy of this software and associated documentation files (the "Software"), 
    to deal in the Software without restriction, including without limitation 
    the rights to use, copy, modify, merge, publish, distribute, sublicense, 
    and/or sell copies of the Software, and to permit persons to whom the 
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included 
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
    SOFTWARE.
*/

#ifndef CMRI_h
#define CMRI_h

#define _CMRI_VERSION 1.5 // version of this library
#include <Arduino.h>

class CMRI
{
    public:
        CMRI(unsigned int address = 0, unsigned int input_bits = 24, unsigned int output_bits = 48, Stream& serial_class = Serial);
        void set_address(unsigned int address);

        bool process();
        bool process_char(char c);
        void transmit();

        bool get_bit(int n);
        char get_byte(int n);

        bool set_bit(int n, bool b);
        bool set_byte(int n, char b);

        enum {
            MAX  = 258, // max packet length in bytes (64 i/o cards @ 32 bits each + packet type and address bytes)
            INIT = 'I', // PC is telling us stuff we don't really care about
            SET  = 'T', // as in TX from the PC => Arduino, PC is SETing our status
            GET  = 'R', // as in TX from Arduino => PC, PC is GETing our status
            POLL = 'P', // PC wants to know our status
            STX  = 0x02, // start byte
            ETX  = 0x03, // end byte
            ESC  = 0x10, // escape byte
        };

private:
        enum {PREAMBLE_1,PREAMBLE_2,PREAMBLE_3,DECODE_ADDR,DECODE_CMD,DECODE_DATA,DECODE_ESC_DATA,IGNORE_CMD,IGNORE_DATA,IGNORE_ESC_DATA,POSTAMBLE_SET,POSTAMBLE_POLL,POSTAMBLE_OTHER};

        int   _address;
        int   _rx_length;
        int   _tx_length;
        char    _rx_packet_type;
        char* _tx_buffer;
        char* _rx_buffer;

        Stream& _serial;

        // parsing state variables
        int     _mode;
        int     _rx_index;

        uint8_t    _decode(uint8_t c); // process one character received from serial port
};

#endif
3

The yellow color is make by very quickly alternating between red and green. If you do this fast enough your eyes won't notice. This alternating is done inside led.drive();. If you are polling the sensors, led.drive(); isn't called and the leds aren't alternating for a moment.

Best way would be to use a timer to call led.drive(); every 9ms or less. You could use the timerOne library. I've never used that library, but I think you should use the following code.

#include "TimerOne.h"

...

void setup {
    Timer1.initialize(9000);// call every 9ms
    Timer1.attachInterrupt(callback);

...

void callback()
{
    led.drive();
}

remove all other led.drive calls

  • Thank you, I will try that. The place I got the bipolar library from did mention the 9ms drive thing, but never actually showed how to do it. – Josh Clough Dec 9 '14 at 18:11
  • Alternatively you could put a few led.drive() commands in between the digitalWrites and digitalReads at the end of the loop. I also see your only calling led.drive(). not led2.drive() etc.. The library might not need that, so if it works now, don't change it. – Gerben Dec 9 '14 at 20:03
  • Author of the library here. Sorry for the late reply; hopefully someone else will find this helpful. Yes, you need to call .drive() on each instance of BiColorLED individually; the library doesn't know about multiple instances. (Thus you need to write led.drive(); led2.drive(); etc.) – Wolfgang Dec 6 '15 at 19:43
  • The documentation doesn't say how to call .drive() in the right timeframe because it's up to you. My code style tends to rely heavily on cooperative multitasking, where the loop() function calls subroutines that are expected to do a very small amount of work and then return quickly. Thus for me it is sufficient to stick led.drive() right in the main loop() function, as loop() runs quite fast. If you have slower functions you can either attach it to an interrupt with the Timer1 library or the like; or intersperse calls at strategic points inside the slow functions. – Wolfgang Dec 6 '15 at 19:43
1

You have your LED's across one of the serial I/O lines (Pin 1). Use different pins.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.