Skip to main content

The One Pin Signal System, using an Arduino

The One (RRRduino) Pin Signal System!
Part I NeoPixels:
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. Short video first! 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 2 8 = 256, but since 0 is an acceptable value too, the maximum is 2 8 - 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?

Part II JMRI:

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(top), A(pproach) and C(lear)! 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 Signal Heads and Masts in JMRI and then hook all the Logix or Signal Mast Logic or Simple Signal Logic in to decide what goes green or red when. We then use a little Python script to monitor the Signals and when something changes in a Signal Head’s state, we simply update the Arduino over the serial port. Yes, we convinced JMRI to send the “R,R,Y,G”, that simple. So for the example here, we are going to use Signal Heads, found in PanelPro under Tools→Tables→Signals→Signal Heads, 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 Head'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 head’s appearance Green, Yellow and Red. 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 Red to Yellow, 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 Green (Clear), Yellow (Approach) and Red (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.
The Signal Masts are built from the Signal Heads (select "Signal Head Controlled Mast" for the Mast Driver), so now you can use the Signal Mast Logic to create all the rules for aspects a whole lot quicker than using Logix.


The Python, or maybe better known as a Jython script (since Java is allowing the Python to run) needs to be run AFTER all your signal heads have their Comment values filled in. You will no need to restart PanelPro if you add a Signal Mast, and neither if you change the Comment to update the color in the Signal Head. Don’t think changing the Approach colors in the Comment will automatically update the signal, a signal color update is only sent when the color changes. So change it to Clear and then back to Approach to see the new color. You would use Panels->Run Script…, and select OneWireHeads.py to run it.
The OneWireHeads.py Jython script can be downloaded from the TxNamib web site, as well as the Arduino code (search for OneWireSignals).
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!

Part III: Exciting Photons:

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
  byte aspect = random( 0, ASPECTS );        // pick a random aspect
  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!

Part IV: JMRI controlled:

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

Popular posts from this blog

Signal Masts in JMRI 4.14, for beginners...

Here are all the notes and files from the Plano Train Show Clinic.

You need to have an internal sensor (IMCOMPORT) with the port in the value like COM10 or /dev/ttyUSB0, whatever Device Manager (or /dev/ttyUSBx) shows for the connected Arduino.

If you have Panels -> Script Output open, you will see the message: !!! Please add IMCOMPORT with the port in the value!!!

The basic idea here is to use the Comment in the Signal Head field to define the position of the Pixel in the string commented to the Arduino, as well as the RGB colors to show for Clear, Approach and Stop (00:00FF00:BBAA00:FF0000). With all the Heads in the Table, run the python script and now you can update the colors in the Comment "live", keeping in mind that a message is only sent when a Head changes color.

We then put the Heads in Masts and use the Signal Mast Logic to do the magic. I like the original Panel Editor more and it works fine as long as you keep using Mast pairs, which requires you to create t…

Not RRRduino, unless you have an MQTT connection via Ethernet or WiFi

JMRI code to connect to an MQTT broker, to publish and subscribe to the messages for signal masts.

Of course, we agree to a standard for the MQTT topics, and since you were not here, we settled on "mast.xxxx" where xxxx is a number starting at zero. This MQTT topic is used as the User Name in a JMRI signal mast. Created as a Virtual Mast with the Aspect used as the payload in the message. And, yes, the device under the real mast needs to be programmed with the same name and code for each of the "aspect" payloads to be implemented. A yellow and red LED'd dwarf signal can not do a "Clear" aspect, so make sure "Clear" is maybe set to yellow for "Approach" as well.
Virtual Masts with agreed upon User Names. Comments are for the One Wire Signals on the NeoPixel string (see prior post)
Virtual Mast
The Jython (or Python) code shown here below, is to attach a Listener to each Signal Mast, and to publish a message to the MQTT Broker when …