Using a FTDI adapter as an IR emitter

This post is part 1 of 5 of  Using a FTDI adapter as an IR

[Crazy intro, just talking to myself]

20 years ago, when I first heard about linux, it didn’t attract me much… spending lots ot time compiling the kernel or the drivers on a 486 seemed so crazy, I wasn’t geeky enough for that. Catched the linux train many years later with Ubuntu 6.04 or 6.10 when almost everything “just worked” and a few google searchs were enough when something didn’t work as expected.

And this week I found myself compiling libftdi and LIRC on a LEGO Mindstorms EV3! Gosh… what happened to me?

[end of crazy intro, start of long and probably boring intro]

Two years ago I found a way to use the soundcard of my laptop as a remote controller for my LEGO motors, thanks to LIRC. It worked OK and it even worked with my Android 4.x phone but never worked properly with ev3dev (and, some months later, I found that it also didn’t worked with my Android 5.x phone) and like most everything else I never really gave him a good use.

Last week someone asked at eurobricks forum how to use a HiTech IR controller with EV3. I also found a way to use it with ev3dev (picking up other people work) and once again never really giving it a good use. Hey, but somebody in the forum said it had tried my code! Thats amazing!

So I returned to LIRC and soundcard, decided to update my own howto. LIRC had evolved a bit in last two years and while reading some docs I found that LIRC also supports FTDI adapters. In fact, its extremely easy to make a USB IR emmiter with just a FTDI adapter and an IR LED, not even a resistor is needed!

I already had a few FTDI cables and adapters but they all use the FT232R model and for reliable timings a FT230X is needed. But my “local” supplier had a Sparkfun Beefy 3 that used FT231X, it’s not the FT230X but it seemed similar enough so I gave it a try… and it works!

[end of long boring intro]

So we need a recent version of LIRC that implements ftdix driver. Ubuntu and Debian jessie (so ev3dev also) only have 0.9.0… We need to download the source code directly from LIRC and compile ourselves. I downloaded the last version available, 0.9.4d.

For LIRC to compile the ftdix driver we also need libftdi. Ubuntu and Debian have it but I also compiled it from source – not sure why but at least for ev3dev just installing Debian libftdi packages is not enough and it seems that just compilling libftdi is also not enough, I had to do both.

My EV3 is running ev3dev snaphot 2017-02-06. Two days later there’s already a new snaphot but I only updated with apt:

sudo apt update
sudo apt upgrade
sudo apt dist-upgrade

After reboot my Ev3 is running kernel 4.4.47:

Linux ev3dev 4.4.47-19-ev3dev-ev3 #1 PREEMPT Wed Feb 8 14:15:28 CST 2017 armv5tejl GNU/Linux

First we install all dependencies needed for both LIRC and libftdi:

sudo apt install libftdi-dev build-essential pkg-config xsltproc libusb-1.0 cmake libboost-all-dev

This takes about an hour, lots of packages (mostly related to libboost)

Before spending lots of time compiling it is better to test if out FTDI adapter works as expected so we can download this “hello ftdi” example:

I saved it as “hello-ftdi.c”.

Now we insert our FTDI adapter and look for it at the end of dmesg:

[47800.964059] usb 1-1.2: new full-speed USB device number 8 using ohci
[47801.111337] usb 1-1.2: New USB device found, idVendor=0403, idProduct=6015
[47801.111424] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[47801.111472] usb 1-1.2: Product: FT231X USB UART
[47801.111515] usb 1-1.2: Manufacturer: FTDI
[47801.111553] usb 1-1.2: SerialNumber: DN01DR29
[47801.306754] usbcore: registered new interface driver usbserial
[47801.414060] usbcore: registered new interface driver ftdi_sio
[47801.423075] usbserial: USB Serial support registered for FTDI USB Serial Device
[47801.437994] ftdi_sio 1-1.2:1.0: FTDI USB Serial Device converter detected
[47801.449290] usb 1-1.2: Detected FT-X
[47801.454135] usb 1-1.2: FTDI USB Serial Device converter now attached to ttyUSB0

We see that it is detected and we take note of the idVendor and idProduct values:

idVendor=0403, idProduct=6015

For later use we also take note of its Serial Number:

DN01DR29

Now we edit the “hello-ftdi.c” program and update the idVendor and idProduct.

/* hello-ftdi.c: flash LED connected between CTS and GND.
   This example uses the libftdi API.
   Minimal error checking; written for brevity, not durability. */

#include <stdio.h>
#include <ftdi.h>

#define LED 0x08  /* CTS  (brown wire on FTDI cable) */

int main()
{
    unsigned char c = 0;
    struct ftdi_context ftdic;

    /* Initialize context for subsequent function calls */
    ftdi_init(&ftdic);

    /* Open FTDI device based on FT232R vendor & product IDs */
    if(ftdi_usb_open(&ftdic, 0x0403, 0x6015) < 0) {
        puts("Can't open device");
        return 1;
    }

    /* Enable bitbang mode with a single output line */
    ftdi_enable_bitbang(&ftdic, LED);

    /* Endless loop: invert LED state, write output, pause 1 second */
    for(;;) {
        c ^= LED;
        ftdi_write_data(&ftdic, &c, 1);
        sleep(1);
    }
}

Then we compile our “hello-ftdi” test program:

gcc hello-ftdi.c -lftdi -o hello-ftdi
hello-ftdi.c: In function 'main':
hello-ftdi.c:25:5: warning: 'ftdi_enable_bitbang' is deprecated (declared at /usr/include/ftdi.h:413) [-Wdeprecated-declarations]
     ftdi_enable_bitbang(&ftdic, LED);

We can ignore that warning, as long as we get a “hello-ftdi” binary file.

To run it we need root permissions:

sudo ./hello-ftdi

If we connect a common led between CTS (A) and GND (K) we will see it blink each second so we can replace it with a infrared LED (940 nm is better but 950 nm will also work). And yes, we don’t need to use a resistor.

If we check dmesg again, we notice that our test program disconnected the ttyUSB device, as required:

[15564.602213] ftdi_sio ttyUSB0: FTDI USB Serial Device converter now disconnected from ttyUSB0
[15564.602615] ftdi_sio 1-1.2:1.0: device disconnected

This post was to long for my web server so I broke in several.

 

How to remote control a LEGO DUPLO Train

Some weeks ago, a fellow from my LEGO User Group reached me for some Infrared electronics insights. He was trying to remote control his LEGO DUPLO Train with an infrared remote.

I never played with IR before but got curious. I love the idea of remote controlling something. But not manually, it has to be programmatically so one can use it for automation or robotic purposes. So after some weeks I found the LIRC definitions for LEGO Power Functions IR and used it to remote control LEGO from my laptop via the audio output.

Then it was the anniversary of one the kids and he asked granny for a major upgrade of their DUPLO train track:

LEGO® DUPLO® Deluxe Train Set

So I thought it would be great if they could control the train back and forth… and ordered a used DUPLO Train Locomotive Base (5135c01) from BrickLink.

Opening it was difficult but I managed to do it without breaking anything, thanks to these two previous brave pioneers:

(their locomotives are from a different set but the idea is the same)

The locomotive has a pair of free wheels at front and another pair drived by a motor and some gears at back where there is also a piezo buzzer, a printed circuit board and a pair of wires comming from the batteries holder. There are also two rubber buttons that press some small metal rings against the surface of the PCB to act as switches – the smaller one, hidden in a small hole, makes the sound of fuel being pumped into the tank when the fuel hose is inserted, the bigger one controls starts/stops the train but also indirectly controls the sound of the locomotive (when in movement) and the sound of the breaks (when stopping).

The PCB has 6 wires soldered at points labeled J1 to J6:

  • J1 and J3 – buzzer
  • J2 and J4 – batteries
  • J5 and J6 – motor

As the batteries holder is for just 3 AA batteries, the board works with 4.5V. Not much but enough for a LEGO Power Functions IR receiver or a Vengit SBrick.

We could just dessolder the wires at J2/J4 (power) and J5/J6 (motor), get a LEGO Power Functions cable, cut it in half and solder the tips from one half to J2/J4 and the tips from the other half to J5/J6. But that will make the buzzer useless.

As the buzzer is controlled by an IC on the PCB, I choose to keep the power lines going to the PCB (J2/J4) and deriving from there to the Power Functions cable (but still dessoldering J5/J6). This way, when the batteries are inserted, both the remote and the PCB are ON so the two buttons maintain their functionality. This gives us as an unexpected possibility: we can also use the PF cable to power the train instead of using 3 AA batteries – with special caution because LEGO PF batteries or LEGO 9V batteries give more than 4.5V so some reduction has to be done, like adding some 1N4001 diodes or a voltage regulator… but a USB 5.0V battery pack will be perfect.

Since I didn’t want to drill or cut the plastic, I managed to pass the PF cables through the start/stop button hole (before soldering it) but later found out that it was to tight for the rubber button – so no more «tchoo-tchoo!» nor breaks (some may consider this a feature, not a bug).

Some more photos are available at this Brickshelf folder.

The final result is a LEGO DUPLO train that:

  • can be remotely controlled by Infrared or Bluetooth, manually or programatically
  • can move forward AND backward
  • can use LEGO batteries (PF or 9V) with a special cable or a USB battery pack

IR remote control – speed and position

iConor’s second LIRC configuration file,Combo_PWM.conf, allows us to use LIRC command ‘irsend’ to control the speed of a LEGO Power Functions Motor or the position of a LEGO Power Functions Servo Motor much like the 8879 IR Speed Remote Control.

This shell script sends a sequence of 7 increasingly values then returns to zero and repeat it in the opposite direction:

#!/bin/bash
#0
irsend SEND_ONCE LEGO_Combo_PWM 400B ; sleep  1.2
#1/7
irsend SEND_ONCE LEGO_Combo_PWM 401A ; sleep  1.2
#2/7
irsend SEND_ONCE LEGO_Combo_PWM 4029 ; sleep  1.2
#3/7
irsend SEND_ONCE LEGO_Combo_PWM 4038 ; sleep  1.2
#4/7
irsend SEND_ONCE LEGO_Combo_PWM 404F ; sleep  1.2
#5/7
irsend SEND_ONCE LEGO_Combo_PWM 405E ; sleep  1.2
#6/7
irsend SEND_ONCE LEGO_Combo_PWM 406D ; sleep  1.2
#7/7
irsend SEND_ONCE LEGO_Combo_PWM 407C ; sleep  1.2
#0
irsend SEND_ONCE LEGO_Combo_PWM 4083 ; sleep  1.2
#-1/7
irsend SEND_ONCE LEGO_Combo_PWM 40F4 ; sleep  1.2
#-2/7
irsend SEND_ONCE LEGO_Combo_PWM 40E5 ; sleep  1.2
#-3/7
irsend SEND_ONCE LEGO_Combo_PWM 40D6 ; sleep  1.2
#-4/7
irsend SEND_ONCE LEGO_Combo_PWM 40C7 ; sleep  1.2
#-5/7
irsend SEND_ONCE LEGO_Combo_PWM 40B0 ; sleep  1.2
#-6/7
irsend SEND_ONCE LEGO_Combo_PWM 40A1 ; sleep  1.2
#-7/7
irsend SEND_ONCE LEGO_Combo_PWM 4092 ; sleep  1.2

There is a total of 256 key codes in the configuration file (16 different values for the red plug x 16 different values for the blue plug). I used just the first subset of 16 so the first byte (for the blue plug) is always ’40’ but it doesn’t matter because I connected both Servo and M-motor to the red plug .

The sequence of key codes is not exactly the same as in the configuration file – I reordered the last 7 key codes.

The ‘sleep 1.2’  makes the script wait 1.2 seconds between each command. It is more or less the time the IR keeps the command active (bigger values would make the Servo return to center and the M-motor stop).

Infrared Remote Control

I discover this week that it is possible to use the headphone jack to send IR signals. The intructions are for a iPhone but one can use almost any audio source… even the computer audio card! And it is already suported in LIRC (Linux Infrared Remote Control), their page even includes a simple circuit!

LIRC – audio IR transmitter

Then I also found this lego-lirc repo in GitHub from a guy nicknamed iConor. It has the source code to generate LIRC configuration files for the LEGO Power Functions Infrared protocol… and also two configurations files:

The first file works like the 8885 – PF IR Remote Control and contains the usual FORWARD / BACKWARD / HOLD / BREAK codes; the second file works like the 8879 – PF IR Speed Remote Control and contains the codes to control the speed (duty cycle) of the motors.

So… why not give it a try?

I searched for more info about ‘lego-lirc’ but found nothing so I opened an issue asking iConor permission to publish those two files (and he promptly agreed, thank you iConor!).

My laptop has Ubuntu 14.10 x64. I installed LIRC and the required Portaudio library for using the audio card:

$ sudo apt-get install lirc libportaudio0

When LIRC is being installed it asks for an IR Receiver and an IR Transmitter, I chose ‘none’ twice.

Then I copied iConor configuration files to LIRC folder:

sudo cp Combo_Direct.conf /etc/lirc/lircd.conf
sudo cp Combo_PWM.conf  /etc/lirc/lircd.conf

and edited /etc/lirc/hardware.conf to create a LEGO_Combo_Direct transmitter associated to the audio card output:

(...)
#Chosen IR Transmitter
TRANSMITTER="LEGO_Combo_Direct"
TRANSMITTER_MODULES=""
TRANSMITTER_DRIVER="audio"
TRANSMITTER_DEVICE="hw@96000"
TRANSMITTER_SOCKET=""
TRANSMITTER_LIRCD_CONF="/usr/lirc/Combo_Direct.conf"
TRANSMITTER_LIRCD_ARGS=""
(...)

My audio card accepts sampling at 96 kHz but others may require lower values like 48 kHz.

I also edited /etc/lirc/lircd.conf to include both iConor configuration files

(...)
include "/etc/lirc/Combo_Direct.conf"
include "/etc/lirc/Combo_PWM.conf"
(...)

but I’m not sure if it is really needed since hardware.conf already has the right path.

I am also not sure if it is really needed or not but somewhere between my several tries I had to create a folder for lirc where it writes 2 files (one I think is a lock, the other contains just the process id)

~$ sudo mkdir /var/run/lirc/

Now let us start the lirc daemon:

~$ sudo service lirc start

and check if it is working:

~$ pgrep lirc
1234

and what kind of transmitter does it understands:

~$ irsend LIST "" ""
irsend: LEGO_Combo_Direct
irsend: LEGO_Combo_PWM

and also  what codes are available:

$ irsend LIST LEGO_Combo_Direct ""
irsend: 000000000000010e FLOAT_FLOAT
irsend: 000000000000011f FLOAT_FORWARD
irsend: 000000000000012c FLOAT_BACKWARD
irsend: 000000000000013d FLOAT_BRAKE
irsend: 000000000000014a FORWARD_FLOAT
irsend: 000000000000015b FORWARD_FORWARD
irsend: 0000000000000168 FORWARD_BACKWARD
irsend: 0000000000000179 FORWARD_BRAKE
irsend: 0000000000000186 BACKWARD_FLOAT
irsend: 0000000000000197 BACKWARD_FORWARD
irsend: 00000000000001a4 BACKWARD_BACKWARD
irsend: 00000000000001b5 BACKWARD_BRAKE
irsend: 00000000000001c2 BRAKE_FLOAT
irsend: 00000000000001d3 BRAKE_FORWARD
irsend: 00000000000001e0 BRAKE_BACKWARD
irsend: 00000000000001f1 BRAKE_BRAKE

OK, we’re ready! Now suppose we want to make both motors spin forward:

~$ irsend SEND_ONCE LEGO_Combo_Direct FORWARD_FORWARD

Yeah! The motor(s) spin for about 1 second.

So if we want shorter runs we can send a BREAK or a FLOAT after a short time, say 0.1 second:

~$ irsend SEND_ONCE LEGO_Combo_Direct FORWARD_FORWARD; sleep 0.1; irsend SEND_ONCE LEGO_Combo_Direct FLOAT_FLOAT

I faced some strange behaviours (delays, lags) until I better read the LIRC audio page and saw this warning:

“It takes some time to set up (50 ms or so) so when no clients are connected to lircd the first transmission will have some higher latency.

A workaround for this is to keep irw running with a bash script like this:

 #!/bin/bash
 while [ true ]; do
 irw || true
 sleep 1
 done

(…)”

So i created a ‘lirc-keepalive.sh’ file, gave it execution permissions and executed in the background:

~$ chmod +x lirc-keepalive.sh
~$ ./lirc-keepalive.sh &

And it makes a REALLY BIG difference.

For anyone who might get interested in this, I suggest first trying with just a headphone or 2 common LEDs (I used a pair of red). If there is a click or a blink then use 2 IR LEDs. Mine are L-934F3C – IR Emitter 3mm 940nm (€0.25 each) and no resistor at all but if you want to play safe use a small resistor like 8Ω.

You also need a cable (just cut the cable of a old headphone set) and a soldering iron. I also used heat shrinking tube but any kind of insulation material is OK.

The required materials:

And the completed cable:

Now about the LEDs…

These LEDs are from Kingbright, their specs say

1.2V Typ @ 20mA, 940 nm, 50° angle, 10 mW/sr Typ @20 mA

I also tried a pair of TSAL6100 950nm High Power Infrared Led from Vishay:

1.35V Typ@ 100 mA, 10° angle, 170 mW/sr @100 mA

but I got more or less the same results and as their emission angle is much smaller I needed to point more carefully to the PF RC receiver. so I kept the first pair. The audio output is intended for headphone so it isn’t much powerfull and might not give enough current to notice a difference – or i just made a mistake.

My first tests achieved a respectable 3 meter range. Not bad for something so simple and costing less than €1 – now everyone can programmaticaly control their LEGO models. Kudos for LIRC people for being so open-minded and for iConor for creating this configuration files.

Next I’m going to try the same with a LEGO Mindstorms EV3 and also a Raspberry Pi.

R4T1NH0 – um micro rover LEGO

Sozinho em casa com os miúdos, usei o SBrick para um micro rover telecomandado:

É uma montagem frágil mas estou certo que é possível fazer ainda mais pequeno e ainda assim mais robusto, é uma questão de tempo e engenho.



[actualizado no dia seguinte]

Uma versão ligeiramente melhor em que a ligação das rodas aos micro-motores não gera tanto atrito e onde experimentei utilizar 3 pilhas de lítio CR2430 para fornecer os 9 Volt. As pilhas portaram-se melhor do que eu esperava.


Assim quase do nada tenho aqui um robot de mesa bastante interessante para experiências com o Snap!

[nova actualização]

O consumo do robot com 3 pilhas CR2430 novas varia entre 60 mA (movendo-se para a frente) e 90 mA (rodando sobre si próprio).

SBrick remote control com Snap!

Um controlo remoto simples em Snap! usando as 4 teclas cursoras:

snap-SBrick2A device extension que criei implementa 3 motores: A (canal #1), B (canal #2) e um pseudo-motor P (ambos os canais). Para movimentar o rover para trás e para a frente é usado o motor P, para rodá-lo para a esquerda ou para a direita são usados os motores A e B com direcções opostas.

Depois de uma décima de segunda de rotação dos motores é dado um comando de rotação aos motores com duty cycle de zero (COAST) mas também podia ser usado o comando stop (BREAK).