Large Scale Central

AirWire JMRI Project

After tinkering all morning, I was able to get JMRI to control AirWire receivers. One of the hardware options for connecting JMRI to a wired layout is an open source Arduino-based controller known as DCC++. I had already written a library for the Arduino to talk to the TI CC1101 RF module that is used in the AirWire products, and was able to combine my library with DCC++ to get JMRI’s DCC stream out over RF.

It didn’t work right away for a couple of reasons.

  1. JMRI and DCC++ use 128 speed steps, which requires formatting the DCC packet as an Advanced Operations Instruction rather than the basic speed/direction packet that AirWire uses. I modified the DCC++ library to truncate the 7-bit speed to 5-bit and do some basic manipulation to avoid step 1 (emergency stop), etc.

  2. Still didn’t work. Comparing the signal generated by the T5000 to that generated by the Arduino, I found that the 0 bit duty cycle was about 16% longer coming from the T5000. I modified the prescaler, and my engine started rolling down the track. Both were within the NMRA spec, but the AirWire receiver seems to be fussy about it.

Here is a photo of the contraption.

The parts are:

  1. Arduino Mega Clone
  2. Breadboard Shield
  3. 5 V to 3.3 V bi-directional level converter (in the breadboard)
  4. CC1101 radio module

Total cost if you buy the parts on eBay from China would be under $20.

I used a Mega because the Arduino communicates with the radio module over SPI, and those pins overlap with the output pins used by DCC++. I haven’t explored whether the DCC output pins can be reassigned or if they were chosen because of some specific hardware capabilities.

Once I got the on-screen throttle working in JMRI, I downloaded WiThrottleLite on my iPhone, and connected it to the JMRI WiFi Throttle Server. It works very well. The response is not as fast as the T5000, which is to be expected, but good enough to hand to a visitor.

Next step is to tackle CV programming.

Good work Eric!

You keep saying “AirWire” receiver, but do you mean a G2 receiver/decoder/motor only unit?

So it’s fussy about zeros? interesting, but of course the only thing it needs to be compatible with is AW, right? (https://www.largescalecentral.com/externals/tinymce/plugins/emoticons/img/smiley-tongue-out.gif)

So Airwire is always in 28 SS?

Something you might want to add to your tools sometime (if you can find one) is this DCC packet analyzer: http://www.pricom.com/Trains/DCCTester.shtml

You might be able to pick up a used one, really invaluable for what you are doing, and also testing for errors on a layout or from a controller.

Greg

I’m testing it on a couple of F3 Drop-In boards. I expect to have a G3 here tomorrow, and will try that, too. I don’t know what to expect with the Convrtr - particularly with regard to the 28 vs. 126 speed steps. The T5000 throttle uses 28 steps, using the basic speed/direction packet defined in S9.2. To get 128 steps (126, really), you have to use the advanced format in S9.2.1. I assume most normal DCC decoders understand either, and I don’t know what would happen if the Convrtr received advanced packets. It may just blindly pass them to the decoder.

I have a second Arduino with radio set up in RX mode to analyze the RF/DCC data, and use my logic analyzer to look at the actual waveform. You can see the data in the terminal window below (looks weird because there are no leading zeros). But that’s a neat tool. Easier to put in your pocket than an Arduino and laptop.

Ops mode CV programming seems to work fine. On to service mode…

Eric,

Been reading through most of your Arduino/Airwire post over the last few days with quite a bit of interest. I’m specifically interested in using JMRI and Withrottle for both programming and control of the Airwire boards. One question, what kind of range do you get from the CC1001/Wi throttle, that would be one concern I have. Correct me if I’m wrong, but in theory one should be able to build a throttle of this platform, with a CC1001 and an Arduino?

Thanks

TJ Weber

Hi TJ-

Yes, in theory, it should work. I was using DCC++ on an Arduino Mega, hooked to JMRI. However, there is a branch on DCC++ that works as an Arduino library, so you can build standalone encoders. I haven’t tried that.

I had to make two modifications to the DCC++ code to make it work. First, the CVP receivers only understand 28 speed steps, and DCC++ uses 128 with no backward compatibility. I modified the code to listen for the 128 step commands from JMRI and downsample them (so to speak) to 28 steps. The other thing I found was that the AirWire Dropin board that I was experimenting with is really fussy about bit timing. I had to play around with the zero and one intervals, and even then, I never got it to work consistently. It’s something I’ll circle back to, but I got distracted by other projects.

Essentially, though, you can take the AirDCC library that I wrote and set up the modem to transmit, and then just connect the pin that would normally go to the motor controller shield to the modem instead.

Also, I had to use a Mega because of a conflict between the timers used for the DCC packet generation and SPI needed for the modem. These overlap in the Uno, but not the Mega. There may be a workaround, but I haven’t explored it.

Eric,

So to get a JMRI interface with Airwire, I would just need a Mega, a CC1001, and a 5V-3.3V power converter? And this is also the hardware you use for the accessory decoder?

That’s correct. The accessory decoder will run on any of the boards, since the conflict doesn’t exist in receive mode. I’m using a pro mini for the demo I put together.

If you’re going to York, I’ll have it with me and can show you.

Eric,

Finally received my parts, and and working on assembly now. Do you have any sort of schematic for building this, in terms of what pins on the Arduino and Radio module you used? Also, what modifications to the Accesory decoder code would I need to make for it to transmit? Thanks for all the work you’ve done in figuring this out!

TJ Weber

I didn’t get very far documenting the transmitter because I was having the issues I described above, and put it aside to focus on a couple of other things. The code is almost completely different from the accessory decoder code. The only common element is the AirDCC instantiation and initialization.

It may help to look at how the transmitter and receiver work together. The throttle generates a DCC stream and the decoder decodes it. In a conventional DCC setup, the path from the throttle to the decoder is hardwired through the track. Of course the stream coming out of a microcontroller doesn’t have enough power to move a locomotive, so between the throttle and the track is a booster of some sort that takes the logic level data stream and converts it to a differential, high voltage signal. With AirWire, that path is through a pair of modems instead. It’s the same logic-level DCC stream coming out of the microcontoller, but it’s feeding the modem’s input instead of a booster. Likewise, the decoder is receiving the stream from the receiving modem’s output instead of the track. Neither microcontroller has any knowledge of the face that they are connected by wireless modems. They could just as easily be connected together with a piece of wire.

The CC1101 is a very capable RF modem, with all sorts of packetizing and error correction functions available. AirWire uses none of that stuff. Instead, it puts the modem into asynchronous mode, which means that it just blindly modulates (or demodulates) a data stream. If you put one in async transmit and the other in async receive and put them on the same frequency, they act as a virtual wire.

My code, AirDCC, handles the setup of the modem. It connects to the CC1101 via SPI, and tells it to use async mode, whether to transmit or receive, what frequency to use, and for xmit, what power level to use. That’s all it does. Once the modem is up and running, AirDCC is done until you change channel or power levels. It also has a function to tickle the modem and remind it to keep modeming, but I don’t know if that’s really needed (AirWire does it, so I replicated it). SPI is ONLY used for sending commands to the modem. The DCC goes in or out on the pin labeled GD0.

So going back to the code question: The main code that you run to generate or receive DCC is not mine. I have used both the NmraDcc and DCC_Decoder libraries for receiving, and both work great. If you look at the example program I provided, it has some documentation in it. Basically, AirDCC gets instantiated like this:

AirDcc Modem(enablePin) ;

This creates a instance of AirDCC called “Modem” and the SPI enable pin is defined elsewhere as “enablePin”. The usual SPI enable pin is 10 for the Uno/Mega/Etc.

Now, in the setup(), you have to actually start the modem:

Modem.startModem(channel, tx, power);

The arguments are the channel number (based on AirWire channels 0-16), whether it’s transmitting (boolean), and the power level (also based on AirWire power levels 1-10).

At this point, the modem is doing its thing, and you can run whatever DCC encoder or decoder code you want.

For the encoding, I was playing with DCC++. This is meant to run on an Uno or Mega with a motor shield, which serves as a booster. Whichever pin goes to the main track input on the motor shield would instead go to GD0 on the CC1101. This effectively sends the DCC stream over wireless instead of to a track.

DCC++ receives serial data over USB from JMRI or other host software. If you want to generate the commands locally, you’d have to work out a way to do that. I know there is a library version of DCC++ for this purpose, but I haven’t tried it.

I’m happy to help, but be aware that I’m heading out of the country tomorrow morning for a week, and don’t expect the particular beach I’ll be on to have internet access. I’ll going to disappear until next weekend.

Here is the schematic for the accessory decoder:

Eric

thanks for you responses, I sent you a PM with a few more questions.

Thanks

TJ