Things got weird with oldest kid and all family is under stress. Perhaps even burnout. So programming and other things that require focus are getting delayed, mostly on purpose.
But today I finally took the chance to add a free certificate to my web server. So this blog is now encrypted. Let’s hope I didn’t brake anything…
After showing LIRC controlling different LEGO IR protocol devices, Alexandre Campos suggested that a real LEGO Universal IR Controller should also control the MINDSTORMS RCX and Scout pBricks.
This remote can control the 3 motors of the RCX (or the two motors and the LED of the Scout) and also start/stop programs stored in the pBrick memory. Unlike the other LEGO IR remotes, it’s not possible to choose a channel so the remote will control all pBricks in range.
I captured all motor control signals with Arduino rawRecv and replicated them with rawSend and it worked very well so I thought it will be easy to convert the signal to LIRC format like I did with Spybotics.
Wrong.
Turns out the RCX signals are huge. For instance the “A Forward” key generate a 100 byte signal:
Assuming a 417 µs bit lenght (2400 bps) I just copy these 100 values into a spreadsheet like LibreOffice Calc and use ROUND(x/417) to get the toggling sequence:
The whole sequence represents a 221 bit long signal, that’s really long!
Since transmission starts with a start bit ‘0’ those first 14 numbers represent this sequence:
010101010110111111111100000000011
Does it makes any sense?
The RCX messages always start with a “55 FF 00” header, followed by a payload where each data byte is followed by it’s complement. At the end, a checksum is sent, also followed by it’s complement.
For a “A Fwd” command (“D2 00 08”) the payload is:
D2 2D 00 FF 08 F7
(where 2D, FF and F7 are the complements of D2 00 08, i.e. FF-D2 = 2D, FF-00=FF and FF-08=F7)
And the checksum is “DA 25”
D2 + 00 + 08 = DA
FF - DA = 25
So the complete message is 11 byte long:
55 FF 00 D2 2D 00 FF 08 F7 DA 25
Each one of these bytes is sent in reverse order, with a start bit, an odd parity bit and a stop bit so with 3 extra bits for each we get a 121 bit long signal, much less than the 221 bits I captured.
So what should I had captured?
Let’s see just the header in binary notation:
55 FF 00 = 01010101 11111111 00000000
reverting each byte:
10101010 11111111 00000000
adding odd parity bit:
101010101 111111111 000000001
adding a start and a stop bit:
01010101011 01111111111 00000000011
Now if we re-group everything:
0 1 0 1 0 1 0 1 0 11 0 1111111111 000000000 11
Now we count how many times each bit occurs before “toggling”:
1 1 1 1 1 1 1 1 2 1 10 9 2
Thats quite familiar… let’s see those first 14 values of my captured sequence again:
1,1,1,1,1,1,1,1,1,2,1,10,9,2
Well, just one extra ‘1’ at the beginning, can’t be just a coincidence.
Analyzing the whole toggling sequence, I see two occurrences of the header. So I probably captured two repeated “A Fwd” signals, but the Arduino rawRecv sketch stopped capturing at the 100th sample.
So let’s ignore everything from the second header to the end:
Using this signal with rawSend works so my guess was correct.
I made a python script that calculates the complete signal and also it’s LIRC value from the toggling sequence and it gives a 121 bit long string as expected:
Unfortunately using “0xaadff80325eb4c01bff082ef4b6a91” on a LIRC configuration file doesn’t work and my syslog shows:
lircd-0.9.4d[8847]: Info: Using remote: LEGO_RCX.
lircd-0.9.4d[8847]: Error: error in configfile line 10:
lircd-0.9.4d[8847]: Error: "0xaadff80325eb4c01bff082ef4b6a91": must be a valid (__u64) number
lircd-0.9.4d[8847]: Error: reading of file '/usr/local/etc/lirc/lircd.conf.d/LEGO_RCX.conf' failed
Must be an unsigned 64 bit long value?! Dang!
Searched a lot trying to find a way to use longer values in the LIRC file but all I could find was a workaround, mostly used by air conditioner owners: instead of using hexadecimal codes LIRC also accepts “raw_codes”
The first number indicates the duration of the first pulse in microseconds. The second number indicates the duration of the space which follows it. Pulse and space durations alternate for as long as is necessary. The last duration should represent a pulse.
Turns out this is more or less the same as the rawSend output I got before, except for the last value that it is a gap. So I removed the last value (‘466’) and defined a one-size-fits-all gap (‘2138’, the larger last value I got from all rawRecv captures).
With these 3 files and ANY supported LIRC transmitter I can now control every single IR device ever made by LEGO – even MINDSTORMS EV3 with IR distance sensor since it understands Power Functions IR signals.
This started most as a curious thing and now I realize it went crazy. But I learned a lot so perhaps other may benefit of it.
On my quest to control all LEGO MINDSTORMS models ever sold from a single “gateway” I started a few weeks ago my work with the Spybotics:
This little fellow has 2 motors and a touch sensor inside, can talk IR and also reacts to VLL. So it’s kind of a Micro Scout on steroids. And, with a little of good will, we can call it the ancestor of the LEGO BOOST, among the Cybermaster.
The Spybotics was programmed with a serial-to-optical cable. I still don’t have one so I tried to program it with a RCX USB IR Tower using NQC (there are some reports that the USB version of the IR Tower can “speak” at 38 and 76 kHz and the later is needed for the Spybotics). But no success yet.
The Spybotics also reacts to a IR Remote Control:
The protocol allows 3 different channels and it’s also possible to send commands to all 3 channels simultaneouslys so I decided to get more information about the protocol.
Turns out that this IR protocol was implemented by LEGO a few years before the Power Functions IR protocol but it never gained momentum and it was quickly abandoned. But a few interesting systems used this protocol:
The last one surprised me as I have two of these, after a very lucky finding 4 years ago on our summer holidays – our kids love them and we use them at our LUG events with great success among young visitors:
So I picked up my Manas, Spybotics and DUPLO commands… and yes, they are interchangeable!
It would be great if I could control my kids DUPLO Dozer from the computer, side-by-side with the Spybotics and the Manas.
So let’s find more details about this pre-Power Functions IR protocol.
The best I could find were references to a Rob Doucette document where he gathered info collected from several Lugnet posts (mainly contributed by John Barnes). One of those references is Philo’s site, where he shows how he used is RCX to control the RC Nitro Flash).
Unfortunatelly the original document location no longer exists:
“The transmitters transmit short messages of two bytes encoded as 8 bit + odd parity. The transmissions use 76kHz modulated IR at a baud rate of 4800, 25% duty cycle.”
Each message contains 4 nibbles (4-bit digits):
Channel + Orange + Yellow + Checkdigit
Channel can be 4/5/6/7 (meaning all, #1, #2 or #3)
Orange and Yellow can be 0/7/8/F (no button, forward button, reverse button and stop or immediate off)
And Checkdigit = 10h – (Channel + Orange + Yellow) AND Fh
The space between each message is different according to the channel used (simillar to PF IR protocol):
The document also has a NQC program demonstrating how to use the RCX to control 3 Manas units (each on its channel)
I tried to replicate the commands with an Arduino and an IR LED but all my attempts failed miserably. After getting tired of it, I decided to analyze the signal, but since I don’t own a scope or a logic analyzer I had to use the only tools I have: an Arduino, a Raspberry Pi, a laptop and Google.
So to “sniff” the IR signal I used an Arduino Uno with a TSOP4856 as an IR decoder and a IR LED for testing my signals back. The setup is pretty much the same as this Adafruit lession:
The TSOP signal is connected to Arduino pin 2 and the IR LEDs are connected to Arduino pin 3 (that’s right, two LED’s in serial, no resistors… might use a transistor later).
The TSOP module has a filter better suited for 56 kHz carrier – not quite the 76 kHz I wanted but it was the higher carrier I could found – just for the record, an IR photodiode and a 1M resistor also works, both devices can detect the 76 kHz signal at short distances.
So I installed cyborg5’s IRLib2 Arduino library on my laptop and tried the rawRecv sketch available on the examples folder. Pressing the Yellow Forward button on the Manas command I got this:
Ready to receive IR signals
Do a cut-and-paste of the following lines into the
designated location in rawSend.ino
#define RAW_DATA_LEN 12
uint16_t rawData[RAW_DATA_LEN]={
1082, 206, 154, 238, 178, 590, 594, 238,
178, 658, 178, 1000};
So I passed those last lines into the rawSend sketch and changed the carrier frequency to 76:
mySender.send(rawData,RAW_DATA_LEN,76);
… and each time I sent a key to the Arduino serial terminal my Manas yellow motor rotated forward.
Wow!
I tried to find some meaning to those values but I was totally lost. Tried to use another skech (Analyze) but the results made no sense… I captured all keys combinations and they worked but I wanted to know the meaning.
So I used the same TSOP4856 on my Raspberry Pi and installed LIRC, so I can use “mode2” command to record a signal. Recent stretch-based versions of Raspbian have some quirks but following this method I had success.
$ mode2 -d /dev/lirc0
space 16777215
pulse 1085
space 179
pulse 239
space 174
pulse 239
space 534
pulse 651
space 175
pulse 190
space 644
pulse 190
space 46681
pulse 1090
space 156
pulse 260
space 183
pulse 235
space 526
pulse 658
space 173
pulse 241
space 576
pulse 259
space 1518
pulse 69
space 45041
pulse 1090
space 156
pulse 211
space 225
pulse 190
space 579
pulse 607
space 226
pulse 189
space 643
pulse 195
space 46689
pulse 1090
space 176
pulse 242
space 175
pulse 241
space 495
pulse 676
space 175
pulse 242
space 572
pulse 274
space 46589
pulse 1041
space 210
pulse 204
space 210
pulse 207
space 578
pulse 607
space 207
pulse 224
space 610
pulse 220
space 46642
pulse 1041
space 214
pulse 200
space 227
pulse 190
space 566
pulse 604
space 230
pulse 187
space 661
pulse 172
space 46683
pulse 1038
space 211
pulse 206
space 210
pulse 206
space 561
pulse 207
space 417
pulse 207
space 210
pulse 1042
space 46445
pulse 1038
space 209
pulse 217
space 201
pulse 204
space 584
pulse 173
space 434
pulse 190
space 227
pulse 1027
space 921
pulse 159
space 45364
pulse 1038
space 213
pulse 201
space 230
pulse 187
space 578
pulse 190
space 437
pulse 187
space 230
pulse 1021
I installed IrScrutinizer on my laptop, imported the above result (“Import signal”) and got a nice signal in the “Scrutinize signal” pane:
A zoom of the first block from the left shows a clear sign:
But how does this relates to “50 74” that, according to the protocol definition, is the 2-byte representation for “Yellow Forward on Channel #1”?
Well, after a few head aches, I got it.
First, I realize that I made a stupid beginner error: like many serial codes, the bits are sent in revere order (least significant bit of each byte first, more significant bit last).
Then I understood the rawRecv format: each value represents the time duration of a continuous mark or space before it “toggles”.
I also got confused with the bits representation (that 25% duty cycle still confuses me). But if I assume a single bit duration to be near 208 µs (for a 4800 bit per second rate), ‘zero’ as the start bit and ‘one’ as the stop bit I can translate this:
uint16_t rawData[12]={
1082, 206, 154, 238, 178, 590, 594, 238,
178, 658, 178, 1000};
5 zeros (1082) = start bit + 0000
1 one (206)
1 zero (154)
1 one (238)
1 zero (178)
3 ones (590) = odd parity bit + stop bit + pause
3 zeros (594) = start bit + 00
1 one (238)
1 zero (178)
3 ones (658)
1 zero (178)
2 or more ones (1000) that must include odd parity bit, stop bit and pause
The last value (1000 µs) is an arbitrary value used by the sketch author to end the signal.
So…
00000101011100010111011
start=0 | 0000 1010 | odd=1 | stop=1
pause=1
start=0 | 0010 1110 | odd=1 | stop=1
The odd parity bits are correct, that’s a good start.
Reverting the order of the data bits, we have:
0101 = 5 | 0000 = 0 | 0111 = 7 | 0100 = 4 => 5074
Yeah!
So the precision isn’t pretty (I got bits with just 154 µs and others with 238 µs when they all should be 208 µs long) but that doesn’t really matter – I can now recreate all signals with theory-perfect values and test if it works.
So I wrote a python script to generate all combinations for all channels and also a RemoteControl sketch – just an extended ‘rawSend’ sketch that sends different signals according to the keys received from the serial port.
This is a sample of the output of my python script:
The serial port can be used directly so we can use our keyboard (in linux, just use screen to connect to the Arduino serial port, i.e. ‘screen /dev/ttyACM0′) or we can write our own programs to automate it.
And yes, we can connect the Arduino to our MINDSTORMS EV3 and use ev3dev to control our old LEGO IR devices 🙂
Even better: we can use the same approach to generate a LIRC configuration file so we don’t even need an Arduino – any IR transmitter supported by LIRC will work… including my FTDI IR emitter. That means we can use the same emitter to control all LEGO IR devices, Power Functions and pre-Power Functions:
In 1987 LEGO released several Activity Cards with examples to be used with the Interface A. One particular Activity Card, the 9700b8, proposed a robotic turtle that could be used to draw lines like the Logo Turtle (in fact, Interface A was used used with a LEGO variant of the Logo language) or even a lne follower!
I don’t have the exactly parts to build a 9700b8 robotic turtle but have enough TECHNIC bricks already available at that time so created my own robotic turtle… the Python Turtle 🙂
Controlling motor or lamps is easy, just change the state of a bit and the output changes accordingly. I made a simple cable adapter to use 9V devices so I can use more recent motors, even Power Functions 1.0 motors and LED’s. The PF Medium Motor works fine, just a bit more slow because maximum voltage is around 4.5V instead of 9V.
Read sensors is not so simple. When idle they are always high. When I shorted them the green LED blinks very quickly and then returns to high again.
I have an optosensor and it changes when passing a yellow brick at front (according to the user guides, the optosensor seems to react better to yellow) and it also changes when passing my hand between it and a light source or when pointing a ligth source directly to it. But it doesn’t “keep” that state, after a second or two it returns to high.
So I thing the sensor inputs react only to change, perhaps through a capacitor.
Using the same cable adapter I can use the RCX touch sensors with the Interface A
It works but it seems to only detect releases, not presses.
So for the inputs to be usefull we might have to take in account more than just the current state. And perhaps a counter also helps.
By modern criteria, the Interface A is quite simple: it has 2 inputs that accept TTL levels and 6 power outputs able to turn ON or OFF 6 ‘devices’ like motors or lamps. These 6 output can be used independently or in pairs. A motor connected to a single output just turns ON or OFF so it rotates always in the same direction but when connected to a pair of outputs it can rotate in both directions. I think (but I’m not sure) that this is implemented through an H-bridge – if true, motors can also stop in two ways (Break and Coast).
There are a few web resources with excellent information about the LEGO Interface A:
So the Interface A uses 6 pins from the Data port of the Parallel Printer Interface (D0..D5) for Output and 2 pins from the Status port (Paper Empy and Select) for Input. Two other pins from the Control port (STROBE and AUTO FEED) are used to get the +5 Volt that power the optocouplers that assure electric isolation between the computer circuits and the external world circuits – these are also powered by a 7.5V AC transformer with at least 11 Watt (VA), not easy to find but we can use a good 12V power adapter with at least 1A output (preferably 15V/2A) because there is an internal rectifier bridge [that reduces average voltage to half].
A note about the power adapter: my first attempts were with a cheap linear “universal” wall adapter, just 300 mA output, at 12V. It worked but when issuing commands to motors, every time the motor stopped I saw both Input green LEDs blinking. With a decent switched wall adapter, 2000 mA, also at 12V, never happened again.
Before using the FTDI adapter I manually tested my Interface A, using a 4.5V battery to power the optocouplers (just need to connect one of the two Control pins, they are internally shorted; same for Ground pins). Some jumper wires directly attached to the 20-pin IDC connector and I was controlling a 4.5V Technic motor:
Only issue was finding pin 1: it is the top pin from right. I made a mistake and started with the top pin from left and it started to smell bad – amazingly, it didn’t destroy my Interface A.
So these are the pins (based on the table available at L Gauge)
Pin
Function
1
VCC: +5V
2
–
3
VCC: +5V
4
–
5
Ground
6
Output Bit 0
7
Ground
8
Output Bit 1
9
Ground
10
Output Bit 2
11
Ground
12
Output Bit 3
13
Ground
14
Output Bit 4
15
Ground
16
Output Bit 5
17
Ground
18
Input Bit 6
19
Ground
20
Input Bit 7
Don’t need to use all pins – pin 1 and 3 (VCC) are internally connected, as all Ground pins – if we keep wires short between the FTDI adapter and the Interface A, we don’t need so many Ground wires (on a long flat cable they help minimizing noise). I used just 10 (VCC, GND and 8xI/O).
To use it I need to know the serial number of my FTDI. That’s available from dmesg:
[ 253.177269] usb 1-6.2: new full-speed USB device number 10 using xhci_hcd
[ 253.272003] usb 1-6.2: New USB device found, idVendor=0403, idProduct=6001
[ 253.272012] usb 1-6.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 253.272017] usb 1-6.2: Product: FT232R USB UART
[ 253.272022] usb 1-6.2: Manufacturer: FTDI
[ 253.272027] usb 1-6.2: SerialNumber: A105BPBO
[ 253.317758] usbcore: registered new interface driver ftdi_sio
[ 253.317781] usbserial: USB Serial support registered for FTDI USB Serial Device
[ 253.317912] ftdi_sio 1-6.2:1.0: FTDI USB Serial Device converter detected
[ 253.317949] usb 1-6.2: Detected FT232RL
[ 253.319094] usb 1-6.2: FTDI USB Serial Device converter now attached to ttyUSB3
Or we can use the ‘pylibftdi.examples.list_devices’ provided with the library:
sudo python3 -m pylibftdi.examples.list_devices
FTDI:FT232R USB UART:A105BPBO
(pylibftdi requires root privileges unless we write an udev rule for our device)
So I’m writing a python script that uses the computer keyboard to control the individual output ports 0..5 or the pair ports A..C and constantly polls the state of the input ports 6 and 7.
Nowadays computers are everywhere. And you can connect almost anything to a computer thanks to USB – truly a Universal Serial Bus.
But in 1986 computers were not so omnipresent. Computers were expensive and difficult to connect. Apple had the SCSI bus, IBM had ISA bus and simpler systems like the ZX Spectrum had… something. You could expect to connect maybe a modem or a printer but pretty much everything else would need a custom interface card.
So in 1986 LEGO announced the Dacta Technic Interface A (1093 or 9750) and for the first time people could use LEGO Technic motors and sensors with a computer.
Interface A was meant to be connected to the printer port of the computer. This was a clever move – at that time, the only de facto standards were the serial port (RS232) and the parallel printer port (Centronics). By choosing the printer port several systems could use the Interface A: the IBM PC, the Apple II, the Acorn BBC Micro, the Commodore C64 and the MSX (some would still require a custom adapter).
So, historically speaking, the Interface A was the grand-grand-father of the current LEGO MINDSTORMS, WeDo and BOOST products.
I was 15 by that time. I had a Sinclair ZX Spectrum 128 and was learning Basic and decided to be an Engineer. In Portugal LEGO Technic was rare and expensive and I never heard about Interface A until a few years ago.
At university I tried to make my own Spectrum interface to use some motors but gave up and made a Centronics interface to use with my PC. I was afraid to damage my pretty expensive computer so I spent weeks designing it and used optocouplers to isolate the computer from the external electronics I wanted to use. I didn’t have LEGO motors so I used walkman motors glued to LEGO plates and used rubber bands instead of gears and even made a small 3 DOF LEGO robotic arm with the bricks I had from the only Technic set I ever had before my dark age, the 8074 – Universal Set with Flex System:
So without knowing, I was designing my own Interface A Plus 🙂
So after my LEGO dark age I was back to robotics and LEGO and finally found out the Interface A. Interesting but useless, I don’t have a printer port anymore on any of my computers and I was not interested in making an Arduino adapter just for that.
But recently I had been experimenting FTDI devices with linux and MINDSTORMS EV3. One of the uses of the FTDI 232 familly allows bitbanging each of the pins to create a custom GPIO interface. There even are python libraries for that purpose… so I wondered if I could use an FTDI adapter with the old Interface A? If it worked, I could use the same adapter with my laptop, my Raspberry Pi’s and of course my EV3.
Quite amazing, the very same scripts I used on my last post work ‘out of the box’ on EV3 running last stretch-based snapshot (‘snapshot-ev3dev-stretch-ev3-generic-2017-09-30.img’).
No need to install micropython, not even the micropython-umqtt.simple and micropython-time libraries… everything is already there!
David Lechner had been porting micropython to ev3dev. Not everything is available but the almost instant runtime is really tempting, makes me want to use micropython for everything: on a SSH session to my EV3, less than 2 seconds after I press the ENTER key to start the publisher script, I get a message on my Ubuntu laptop subscriber.
If we want to use our system as a MQTT broker, we need the mosquitto daemon. The mosquitto-clients package include commands to test publishing and subscribing from the command line:
from umqtt.simple import MQTTClient
def main(server="localhost"):
c = MQTTClient("umqtt_client", server)
c.connect()
c.publish(b"foo_topic", b"hello")
c.disconnect()
if __name__ == "__main__":
main()
This example publish “hello” message to topic “foo_topic” on a broker on our localhost. If we have installed ‘mosquitto’, it is already started (at least on Ubuntu) so we just need to subscribe for messages under “foo_topic”:
mosquitto_sub -h localhost -t foo_topic
We can now run the example (on another terminal):
./micropython example_pub.py
and we should get:
hello
Now let’s use a public MQTT broker. There are several available but since we are already using mosquitto client we will also use mosquitto own public broker (i.e. ‘test.mosquitto.org‘).
Since it is a public broker, it has considerable traffic so we use a less general topic:
from umqtt.simple import MQTTClient
SERVER='test.mosquitto.org'
def main(server=SERVER):
c = MQTTClient("umqtt_client", server)
c.connect()
print("Connected")
c.publish(b"ev3dev_topic", b"Make my day!")
c.disconnect()
print("Sent")
if __name__ == "__main__":
main()
and it still works. It’s not instant messaging but it sure takes less than a second (please note that a public broker cannot assure 100% availability or performance; even it the service is available performance can be degraded if lots of publishers and subscribers are connected passing lots of messages).
Now let’s do the opposite – subscribe with micropython. We use another example, ‘example_sub.py’:
import time
from umqtt.simple import MQTTClient
# Publish test messages e.g. with:
# mosquitto_pub -t foo_topic -m hello
SERVER = "test.mosquitto.org"
# Received messages from subscriptions will be delivered to this callback
def sub_cb(topic, msg):
print((topic, msg))
def main(server=SERVER"):
c = MQTTClient("umqtt_client", server)
c.set_callback(sub_cb)
c.connect()
c.subscribe(b"ev3dev_topic")
while True:
if True:
# Blocking wait for message
c.wait_msg()
else:
# Non-blocking wait for message
c.check_msg()
# Then need to sleep to avoid 100% CPU usage (in a real
# app other useful actions would be performed instead)
time.sleep(1)
c.disconnect()
if __name__ == "__main__":
main()
For this script to run we also need ‘time’ library for micropython: