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.
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:
Team
Code
LEFT
129
FORWARD
130
Right
131
Spot
132
DOCK
133
CLEAN
136
PAUSE
137
Power
138
FORWARD_LEFT
139
FORWARD_RIGHT
140
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:
FTDI FT232RL
Atmel ATtiny2313-20SU in a SOIC20 package
7.3728 MHz Quartz
4 capacitors 0.1 on microfarad SMD0805
2 capacitors at 20pF
1 tantalum 10 ÎĽF capacitor
1 SMD LED (size 1206) to indicate the operation of the FT232 (optional)
1 SMD 560 ohm resistor for the LED (again, optional)
1 IR LED, I used L-34F3C
1 SMD 100 ohm resistor for IR LED.
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:
To send a command, you need to type the corresponding character in the terminal:
Team
Symbol
LEFT
L
FORWARD
F
Right
R
Spot
S
DOCK
D
CLEAN
C
PAUSE
P
Power
W
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.