📜 ⬆️ ⬇️

We control the iRobot Roomba robot vacuum cleaner via IR


Before the new year, I had a useful pet - iRobot Roomba 630. This is the most idle Roomba model without the functions of a scheduler. In general, I do not need these functions, I would like to control the robot from a PC, then it would be possible to run it remotely. To control the robot, the iRobot company produces an IR remote control, “That's it!” - I thought and decided to try to make an IR transmitter to control Roomb-th. All interested in asking under the cat!



How it works

Of course, the first thing I began to look for on the Internet is the Roomb exchange protocol. There is not much information, but I found the most detailed description on the forum www.robotreviews.com , in particular, an interesting message , here is its abbreviated quote:
The remote control IR stream consists of 8 bits each 4ms in duration. According to the paper, each bit is started by a 1ms low period. If the value is 0, the pulse stays low for 2 more ms. If the pulse goes high for 2ms, if the value is 1.

Also, the author lists the codes he found teams
Remote Button IR Stream Sensor Code
Left: 10000001 129
Forward: 10000010 130
Right: 10000011 131
Spot: 10000100 132
Max: 10000101 133
Clean: 10001000 136
Pause: 10001001 137
Power: 10001010 138
Forward / Left: 10001011 139
Forward / Right: 10001100 140
Docking station: Behind: 11110010 242
Docking station: Right: 11110110 246
Docking station: Slightly right: 11110111 247
Docking station: Left: 11111010 250
Docking station: Slightly left: 11111011 251
Docking station: Middle: 11111110 254
')
=== codes found by me ... not exactly sure ===
Docking station: Distant ???????? 248
Docking station: Distant ???????? 244
Docking station: Distant ???????? 240
Not sure ???? ???????? 252

With the specified intervals, it did not work for me and I went the other way: on the same forum, the codes for irshell were uploaded to manage with the PSP .
Here they are
TITLE = Roomba
UP = Up
0000 0069 0000 0008 0070 0027 0023 0070 0023 0070 0023 0070 0023 0070 0023 0070 0070 0027 0023 030B
LEFT = Left
0000 0069 0000 0008 0070 0027 0023 0070 0023 0070 0023 0070 0023 0070 0023 0070 0023 0070 0070 02C8
RIGHT = Right
0000 0067 0000 0008 0072 0024 0024 0072 0024 0072 0024 0072 0024 0072 0024 0072 0072 0024 0072 02D0
CIRCLE = Spot
0000 0067 0000 0008 0074 0024 0024 0074 0024 0074 0024 0074 0024 0074 0074 0024 0024 0074 0024 0314
CROSS = Clean
0000 0069 0000 0008 0071 0027 0023 0071 0023 0071 0023 0071 0071 0027 0023 0071 0023 0071 0023 030B
TRIANGLE = Dock (MAX on older models?)
0000 0069 0000 0008 0070 0027 0024 0070 0024 0070 0024 0070 0024 0070 0070 0027 0024 0070 0070 02C8
L_SQUARE = ​​Power off
0000 0069 0000 0008 0070 0027 0023 0070 0023 0070 0023 0070 0070 0027 0023 0070 0070 0027 0023 030B
L_TRIANGLE = Pause
0000 0069 0000 0008 0070 0023 0023 0070 0023 0070 0023 0070 0070 0023 0023 0070 0023 0070 0070 02C8

Here, the commands are written in hexadecimal codes, this is the so-called Pronto IR format. I found a good description ( Yandex.Disk - just in case) their recording turned out to be quite simple.
Some illustrations from the format description.



Consider the example of the Clean command, because She was for me the most interesting:
0000 0069 0000 0008 0071 0027 0023 0071 0023 0071 0023 0071 0071 0027 0023 0071 0023 0071 0023 030B

0000 - Tells us that this is Pronto RAW format, illustrations of its records above.
0069 - Carrier frequency: 69 16 = 105 10 ; f = 4.145146 MHz / 105 = 39.477663 kHz.
0000 - This means there is no starting recurring command.
0008 - A repeated command consists of 8 bits.
Next is the recording of a repeating command, in the form of the number of periods of the carrier frequency.
0071 - The first pack with a length of 71–16 periods, i.e. approximately 2862 μs.
0027 - We don’t transfer anything 27 27 periods, i.e. approximately 988 μs.
Further - by analogy. It turns out that the signal should look like this:

If the first pack is one and the second is zero, then the command codes from the message from the forum are correct:
TeamCode
LEFT129
FORWARD130
Right131
Spot132
DOCK133
CLEAN136
PAUSE137
Power138
FORWARD_LEFT139
FORWARD_RIGHT140

Manufacturing

Of course, you could buy any IR transmitter connected via USB and feed the above mentioned Pronto Raw codes to the special program and that's it! I didn’t like this idea, at first I wanted my IR transmitter to be used separately from a PC, i.e. It should be with buttons and a battery, but then I decided to leave the remote with the button for later. I wanted the transmitter to be controlled via USB-UART and that it understands just text commands, then it could even be connected to the router.

The scheme turned out pretty simple:



Components:

The signal to the IR diode is fed through the 5 pins of the MK, of course, it would be better to control the transistor, but I wanted to make the circuit as simple as possible. When choosing a resistor R2, you need to remember that the maximum current for ATtiny2313 is 200 mA for all pins, and for 1 output is 40 mA.

The information LED is connected to the CBUS3 pin of the FT232 chip; by default, the PWREN # signal is output to it, i.e. the LED is on when the FT232 is connected to the PC and initialized. Another function can be assigned to this output, for example, RXTXLED # - then the LED will blink during data transfer. This can be done using the FT_PROG utility. Utilities for Linux that can change the purpose of the conclusions I did not find something.

Since at home I use Ubuntu OS, I decided to part the board in KiCAD (this is certainly not Altium Designer, but it does its job), I used all the components from the standard libraries:



Archive with KiCAD project: Remote_USB_PCB.zip
The fee is one-sided, etched by the LUT method.
It is worth noting that I specifically chose a controller in the SOIC package, since it is easier to place it on the board and it is also easier to poison / solder. But the distance between the legs of the FT232RL is quite small, so after transferring the toner to the board, before etching, you need to clear the distance between the leads from the paper residue with some sharp object. I was too lazy and did not do it, as a result I had to cut the grounds in some places, otherwise they merged.

Because of my desire for simplicity, the board was originally quartz-free, and looked like this:

But, apparently, the stability of the internal RC-generator was not enough and Roomba did not react to the commands, although the oscillograms of several periods showed that everything was fine. As a result, I primed the quartz on trimming the unnecessary board from above:


Programming

The program is written in C (AVR-GCC), wrote mainly in CodeBloks, but once it had to be debugged in Atmel Studio. The code does not claim to be “correct” and “beautiful” at all, t.ch. I ask you to take this into account and not to criticize strongly (but useful instructions are welcome).
Listing
 /*  - Irobot Roomba    USB-UART (FTDI FT232R).    Atmel ATTINY2313. FUSE-: Fuse Low Byte: CKDIV|CKOUT|SUT|SKSEL| 0 | 1 |10 |0100 | 0x64 Default 1 | 1 |10 |1100 | 0b11101100=0xEC   7.3728  */ #define F_CPU 8E6 //#define __AVR_ATtiny2313__ #include <avr/io.h> //#include <stdio.h> //#include <stdint.h> //#include <stdlib.h> //#include <util/delay.h> //#include <string.h> //#include <avr/pgmspace.h> /*------<>-----*/ // 39477  //39500   25,3  #define P_GEN {TCNT0=0; PORTB |= 0x1F; while (TCNT0 < 93); PORTB &= ~0x1F; while (TCNT0 < 186);} #define TX_LFCR tx_uart(0x0A); tx_uart(0x0D); //Roomba : #define LEFT 129 #define FORWARD 130 #define RIGHT 131 #define SPOT 132 #define DOCK 133 #define CLEAN 136 #define PAUSE 137 #define POWER 138 #define FORWARD_LEFT 139 #define FORWARD_RIGHT 140 /*------<   >------*/ volatile unsigned char data; unsigned char status; /*----------------<:>----------------*/ void init(void) { //    /1: //CLKPR = (1<<CLKPCE); //CLKPR = (0<<CLKPS0)|(0<<CLKPS1)|(0<<CLKPS2)|(0<<CLKPS3); // : PORTD = 0x0; DDRD = 0x0; PORTB = 0x0; DDRB = 0xFF; // UART: UCSRA = 0x0; UCSRB = (1<<RXEN)|(1<<TXEN)|(0<<UCSZ2); UCSRC = (0<<UMSEL)|(0<<USBS)|(1<<UCSZ1)|(1<<UCSZ0); //UBRR = Fosc/16/BR - 1 UBRRH = 0x0; UBRRL = 23; //  UART- 19200 } void ir_tx(unsigned char data_ir) { /* RAW Pronto    Clean: 0000 0069 0000 0008 0071 0027 0023 0071 0023 0071 0023 0071 0071 0027 0023 0071 0023 0071 0023 030B  : 1: __________ 2862uS |988uS |_____ 0: _____ 887uS| 2862uS |__________    ~16870 uS */ int cnt,repeat; // : TCCR0B = (0<<CS12)|(0<<CS11)|(1<<CS10); //   f/1 TCCR1B = (0<<CS12)|(1<<CS11)|(1<<CS10); //   f/64 for (repeat=0;repeat<10;repeat++) { for (cnt=0; cnt<8; cnt++) { TCNT1=0; if (data_ir & (0x80 >> cnt)) { // 1 while (TCNT1 < 330) P_GEN; while (TCNT1 < (330+114)) ; } else { // 0 while (TCNT1 < 113) P_GEN; while (TCNT1 < (330+114)) ; } } TCNT1=0; while (TCNT1 < 1950) ; } } void tx_uart(unsigned char tx_data) { UDR = tx_data; while (!(UCSRA & (1<<UDRE))); } void tx_help(void) { int sc; const unsigned char help1[] = "Use:CSDPWLRF < >"; for (sc=0;sc < 23;sc++) { tx_uart(help1[sc]); } } /*-----------------<  >-----------------------*/ int main(void) { init(); for(;;) { while (!(UCSRA & (1<<RXC))) ; //  status = UCSRA; data = UDR; switch (data) { case '+': PORTB |= (1<<PB6); break; case '-': PORTB &= ~(1<<PB6); break; case 'c': case 'C': ir_tx(CLEAN); TX_LFCR break; case 's': case 'S': ir_tx(SPOT); TX_LFCR break; case 'd': case 'D': ir_tx(DOCK); TX_LFCR break; case 'p': case 'P': ir_tx(PAUSE); TX_LFCR break; case 'w': case 'W': ir_tx(POWER); TX_LFCR break; case 'l': case 'L': ir_tx(LEFT); TX_LFCR break; case 'r': case 'R': ir_tx(RIGHT); TX_LFCR break; case 'f': case 'F': ir_tx(FORWARD); TX_LFCR break; case '<': ir_tx(FORWARD_LEFT); TX_LFCR break; case '>': ir_tx(FORWARD_RIGHT); TX_LFCR break; default: tx_help(); TX_LFCR break; } } } 


Archive with the project: Roomba_Remote_USB.zip | Separately hex file
Stitched the USBasp programmer using the avrdude utility.
To flush the Fuse bits, run with the following parameters:
 avrdude -p t2313 -c usbasp -U lfuse:w:0xEC:m 

To flash the hex file:
 avrdude -p t2313 -c usbasp -U flash:w:./bin/Debug/Roomba_Remote.elf.hex 

To send a command, you need to type the corresponding character in the terminal:
TeamSymbol
LEFTL
FORWARDF
RightR
SpotS
DOCKD
CLEANC
PAUSEP
PowerW
FORWARD_LEFT<
FORWARD_RIGHT>

There is one more undocumented function - see listing. ;-)
The controller responds to the correct command with a newline, to the incorrect short help.

A small video demonstration of the work:


Now I am not satisfied with the range of work: you need to send the LED exactly to Roomb-y, and this is not very convenient. In this connection, I am thinking of replacing the resistor R2 with a resistor with a lower rating, for example, 68 Ohm, or changing the IR LED.
I am also thinking about a regular remote with buttons, and therefore there is a poll below.

PS
Spelling errors and design errors please report through the LAN.

Source: https://habr.com/ru/post/168033/


All Articles