one-pin-signals


The One RRRduino Pin Signal  System!


Yes, you read that right, you can control a whole signal system with up to 200 lights by using a single Arduino pin. And this was not my idea, all the credit and the patent goes to my friend Tom, but since I already had something using the same technology up and running using only a Nano, I do not feel too bad telling you about it.

There is a tiny 8 pin chip out there called a WS2811 which is a three channel LED driver, and you might find them on the interweb as NeoPixel LED Driver Chips. They need power and ground, typically 5 Vdc, and data to be put in the three registers inside the chip, one for each LEDs it can control. So, in most cases out there today, the LEDs connected are Red, Green and Blue (RGB for short) and by setting the three registers each with an 8 bit value (which in English means a value from 0 to 255, since 28 = 256, but since 0 is an acceptable value too, the maximum is 28 - 1 or 255), you can create more than 16 million colors with these RGB LEDs. Waau, you say, who wants that, over spec'd and overpriced? No, it gets better, there is also a WS2812, where the WS2811 chip is embedded inside the RGB LED package, so three LEDs, a chip to drive them at different levels, included on a small PC board and only $18 for a 100 pieces!!!


NeoPixels and Pixel lights are now famous for putting a string up on your house during Christmas and instead of making the whole string green or red, you can make each individual element a different color. I know, unbelievable, but I have my YouTube video as proof. I don't like heights that much, so I put the strings behind the facia board on my house, so I can keep them up and do Pink and Red for Valentine's Day, Green and Green for St Patrick's Day, Red, White and Blue for Independence day, and of course Purple and scary Orange for Halloween. My oldest daughter even looked up the official 2017 color, so we had some kind of Greenery for New Year's Day! The strings are not exactly cheap yet, about $15 to $24 for a 50 element string, measuring anything from 6 to 12 feet, depending on the element spacing.


So, how do they work? You already know that the chip has 3 registers and each LED needs 8 bits, so 24 bits of data are sent in, and I say sent in and not clocked in, since there is no line or pin for a clock. A data bit by itself has a high part and a low part and by having the high a certain length determines if a 1 or a 0 is transferred into the chip. So by changing the data line twice per bit, embeds the clock for you. And here is the magic about the data passed along, imagine you sit at a big picnic table and uncle Joe scoops the ice cream at the one end and asks everyone to pass the bowls on to the next person...but, with the small difference that you don’t pass the first one you get on, you put it down in front of you and pass everything else on. And when there are no more bowls coming, you can dive in! That is exactly how the 2811 and 2812 chips pass the data from one to the next. The first chip takes the first 24 bits for itself from its input pin and on its output pin it sends everything else along to the next...who in turn takes the first 24 bits, and again, sends the rest on. When there is nothing more coming for a certain amount of time, it loads the data into its LED drivers and the LEDs show their colors. This also ensures that all the LEDs, along the whole string, updates very close to the same time.


Who knows how many LED sets or pixels are in the string, no one, only the guy sending the data out needs to make sure it sends enough data out.  At the picnic, Johnny at the end, would not get ice cream if uncle Joe did not scoop some for him!


What is really nice in this kind of system, if you want to add a signal anywhere in the middle, all you need to change is the software sending the bits out, by inserting the needed 24 bits in the appropriate place in the whole data stream. Ready for a schematic?



In the breadboard view above (thank  you again fritzing), the power source and data is from the USB port, and a similar warning here as with the servos being driven from an Arduino, when you load a Nano or Uno with too many current drawing devices, you need to add a stronger 5V power source to the Vin pin.  The regulator on the Arduino board can only do so much. Here 4 pixels will do just fine, but switch to plan ‘B’ when you hook 100 pixels up!


Pin D3 is connected to the first WS2811’s Data-In pin (ochre color), and this is where uncle Joe is going to send the ice cream to. LED1 will then pass the next 24 bits and more to LED2 (blue wire), which in turn will send the 3rd set of 24 and more to LED3 (green wire), you get the idea, right?


Where do these bits come from and why are we using RGB LEDs, we, model railroaders, do not need blue? Last question first, they are $1.69 a piece when you buy 100, and you never need to turn the blue on, :). Red and green are obvious, and yellow is done with red and green both on. You can also increase or decrease either the red or green amount to get the special yellow flavor for the fall of ‘69. Hope they had signals back then?!? Where do the bits come from, well, the Arduino is going to send them in groups of 24 and will format the 1’s and 0’s the way they need to be.  But who decides which signal showing Stop, Approach or Clear? You do! You could either have the Arduino IDE’s terminal window open and then you type R,R,Y,G and send it, to make our 4 LEDs red, red, yellow and green, or you could use S, A and C! Well, I know, this ain’t going to happen for long, as the layout owner you have other things to do...ok, just ask your dispatcher to do it then!  So all jokes aside, you could either invent your own simple signal-decision system using more Arduinos and we will certainly publish your idea here if you like, or we could fall back on our trusted JMRI.

 


We simply create all the signals in JMRI and then hook all the Logix or Simple signal logic in to decide what goes green or red when and so we use a little Python script to monitor the signals. When something changes in a signal’s state, we simply update the Arduino over the serial port.  Yes, we make JMRI send the “R,R,Y,G”, that simple.  So for the example here, we are going to use Signal Masts, found in PanelPro under Tools→Tables→Signals→Signal Masts, and we are using the “Comment” field to make sure the signals are represented in the order we want them on the wire string. LED1 is always grabbing the first 24 bits, so we need to make sure that Mast’s data is sent first by the Arduino.  


In order to make things a little harder, since you are so super smart, I am going to use a hexadecimal number for the order in the string, and then 3 more sets of hexadecimal numbers to set the red, green and blue values in the Arduino. This will be used for that signal’s aspects Clear, Approach and Stop.  Why, because you can use the this to adjust the yellow of ANY signal individually to your liking by simply changing a comment in JMRI. You can also use an LED as a street light and make it go white, since we will send the blue value as well! Again, instead of you sending R,R,Y,G, we are going to send “00FF0000,01FF0000,02FFFF00,0300FF00” to accomplish the same and for a white one for LED5: 04FFFFFF (RGB with all three 100% on).  


We will also be able to update only 1 LED at a time instead of sending the whole list every time. So when the second signal changes from Stop to Approach, all we need to send is “01BBDD00”, where Red=0xBB, Green=0xDD and Blue=0x00, my first attempt for yellow.




The System Name and User Name have no play here (need to wait for a future article), the only numbers that matter are in the Comment column where the first two digits in hexadecimal specify the position of the LED in the wire string, and the RGB parts between the colons specify the colors for Clear, Approach and Stop. And you can change any or all of them, mine are exactly the same because I am lazy.  An Unlit aspect is obviously dark or black, so we would just send 03000000 to turn all the LED parts off at the 4th signal.  


There is now a Python, or maybe better known as a Jython script (since Java is allowing the Python to run) that you would need to run AFTER all your signals have their Comment values filled in.  You will need to restart PanelPro if you add a signal mast, but not if you change the Comment to update the color. Also don’t think changing the Approach colors in the Comment will automatically update the signal, the signal color update is only sent when the aspect changes. So change it to Clear and then back to Approach to see the new color.  You would use Panels->Run Script…, and select OWS.py to run it.


The OWS.py Jython script can be downloaded from the TxNamib site, no need to confuse our Embedded Engineers, we will stick with Arduino code here. Keep in mind that we are simply looking for a text string coming across the serial interface, then update our list of “pixels” with the color or colors, and then send the 24 bit values for each pixel out on our one pin. And, we are also going to keep it simple and use the NeoPixel library from Adafruit, to avoid making the pin high and low at specific times, their code already works!


Let’s do some basic fun things first, random aspect colors on our signals, so you can get an idea what is at the heart of this.  We include the NeoPixel library from Adafruit. ( If the library is not on your computer yet, go to Sketch→ Include Library→ Manage Libraries and then search for “neopixel” and select and install the Adafruit NeoPixel one. I did not need to restart my Arduino IDE, but certainly the first thing to try if the compiler complains about a missing .h file. ) Then we define a few constant things like the number of aspects to choose from and the number of signals in the string. We create the onepin as an Adafruit_NeoPixel object with the number of signal or pixels in the string, the pin the wire will leave the Arduino on and the type and rate of the transmission.  I have always used the default, no need to bother here.  Then we create all the colors we need, since it is easier to do that than remembering 255, 215, 0 as yellow, and since it is all in one place, we can easily change the colors to our liking. We then put all the colors in an array, just so we can randomly pick one easier.


#include <Adafruit_NeoPixel.h>

#define ONE_WIRE_PIN   6

#define SIGNALS        5

#define ASPECTS        5

#define BRIGHTNESS    10

#define VERSION "OnePinSignals r0.02"
#define BAUD      115200


// 8 signals on ONE_WIRE_PIN
Adafruit_NeoPixel onepin = Adafruit_NeoPixel( SIGNALS, ONE_WIRE_PIN, NEO_GRB + NEO_KHZ800 );

// define all the colors we might need
uint32_t red     = onepin.Color( 255,   0,   0 );
uint32_t yellow  = onepin.Color( 205, 125,   0 );
uint32_t green   = onepin.Color(   0, 255,   0 );
uint32_t lunar   = onepin.Color( 255, 255, 255 );
uint32_t unlit   = onepin.Color(   0,   0,   0 );

// create an array of them
uint32_t colors[ASPECTS] = { green, yellow, red, unlit, lunar };

// like alway, all the one time setup parts
void setup( ) {
 onepin.begin( );
 onepin.show( ); // Initialize all signals to dark or unlit
 Serial.begin( BAUD );

 Serial.println( VERSION );
} // setup( )

// and we loop forever
void loop( ) {
 byte n = random( 0, SIGNALS );            // pick a random pixel to change
 byte aspect = random( 0, ASPECTS );       // pick a random aspect for that signal n
 onepin.setPixelColor( n, colors[aspect] ); // set signal n to aspect
 onepin.setBrightness( BRIGHTNESS );        // too bright is bad
 onepin.show( );                            // show it
 delay( 1000 );                            // wait 1 second
} // loop( )


In setup( ) we set the NeoPixel string up with begin( )  and then show( ) the pixels while the are still blank to clear the string out. And then, the default, nothing new, print the version of this code, so in 2 years, you can tell what was burnt into your Arduino board.


The fun part is in loop( ) where we calculate a random number from 0 to less than SIGNALS (or 5). A number is chosen from 0, 1, 2, 3 or 4, since the first signal is at position 0. And the 5th one is at position 4.  Yes all Greek is seams, but be at ease, it is as simple as 0 being the first number and thus pointing to the first signal.


We do the same for picking an aspect, from 0 to ASPECTS-1, so in the next line, we can assign signal n the color from the array at position aspect.  We do some magic to prevent an eyesore and reduce the brightness of the pixel, since these guys were invented for outdoor things. The show( ) function will update the string again and then we wait one second before we start all over again.


For homework, change the “randomness” to have fake ABS signals that would go from red to yellow to green and back, so it looks like a train is moving down the wire too, filled with ice cream!



Now for the JMRI capable version ... first we need to read values in from the serial port, and as they come in, we update the signal colors. Once a whole string is in, we will update the LEDs on the string:

// like always, all the one time setup parts
void setup( ) {
 onepin.begin( );
 onepin.show( ); // Initialize all signals to dark or unlit
 Serial.begin( BAUD );

 Serial.println( VERSION );
} // setup( )


void loop( ) {
// if serial available
// set bool changeComing
// read serial data (might be more than one signal with colors, so loop
// if data has nRGB format
// create color from RGB
// set signal (int)n to color RGB
// if changeComing
// update string
// changeComing = false
;
} // loop( )

And then the Jython script, to attach the Listeners to the Signal Masts, so we can get a change noticed and a serial string sent when aspects change:


import sys
import time
from javax.swing import JButton, JFrame

class SignalChanger( java.beans.PropertyChangeListener ):
'SignalMastChanger 1.0'

# when a property changes
def propertyChange( self, event ):
print( event.propertyName )

// get the list and add a listener to each
list = signals.getSystemNameList( )
for name in list:
listener = SignalMastChanger( )
signals.getSignalHead( name ).addPropertyChangeListener( listener, "SignalMastChanger", "SignalChangerRef" )


Homework, add another part to the RGB colors in the Comment column to show if that aspect needs to be flashing or not, a simple 00 and 01 would suffice for solid and flashing, and then you change the Arduino code to toggle that LED at position x at a decent rate, like 2 Hz (twice per second). Remember that you would need to send ice cream to all the guys in front of the one that needs the toggling chocolate sprinkles every half second, but you don’t have to update the ones behind him!
Comments