Перед новым годом у меня появился полезный питомец — iRobot Roomba 630. Это самая простоя модель Roomba без функций планировщика. В общем-то мне эти функции не нужны, больше хотелось управлять роботом с ПК, тогда бы можно было его запускать удалённо. Для управления робототом фирма iRobot производит ИК-пульт, «Вот оно!» — подумал я и решил попробовать смастерить ИК передатчик для управления Roomb-ой. Всех заинтересовавшихся прошу под кат!
Как это работает
Конечно же первым делом я начал искать в интернете протокол обмена Roomb-ы. Информации не много, но самое детальное описание я нашел на форуме www.robotreviews.com, в частности интересное сообщение, вот его сокращенная цитата:
The remote control IR stream consist 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. The bit is ended by sending a 1ms high burst.
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
С указанными интервалами у меня не заработало и я пошёл другим путём: на том же форуме выкладывались коды для irshell для управления с PSP.
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
Здесь команды записаны кодами в шестнадцатеричной системе, это так называемый Pronto IR формат. Я нашёл неплохое описание (Яндекс.Диск — на всякий случай) их запись оказалась довольно простой.
Рассмотрим на примере команды Clean, т.к. она для меня была наиболее интересна:
0000 0069 0000 0008 0071 0027 0023 0071 0023 0071 0023 0071 0071 0027 0023 0071 0023 0071 0023 030B
0000 — Говорит нам о том, что это Pronto RAW формат, иллюстрации его записи выше.
0069 — Частота несущей: 6916=10510; f=4,145146 Мгц /105 = 39,477663 кГц.
0000 — Значит стартовой не повторяющейся команды нет.
0008 — Повторяющаяся команда состоит из 8 бит.
Далее уже идет запись повторяющийся команды, в виде количества периодов несущей частоты.
0071 — Первая пачка длительностью 7116 периодов, т.е. примерно 2862 мкс.
0027 — Ничего не передаем 2716 периодов, т.е. примерно 988 мкс.
Далее — по аналогии. Получается, сигнал должен иметь такой вид:
Если первая пачка — это единица, а вторая нуль, то коды команд из сообщения с форума верны:
Команда | Код |
---|---|
LEFT | 129 |
FORWARD | 130 |
RIGHT | 131 |
SPOT | 132 |
DOCK | 133 |
CLEAN | 136 |
PAUSE | 137 |
POWER | 138 |
FORWARD_LEFT | 139 |
FORWARD_RIGHT | 140 |
Изготовление
Конечно можно было купить любой ИК передатчик с подключением по USB и скормить специальной программе выше приведенные Pronto Raw коды и всё! Такая идея мне не пришлась по душе, по началу я хотел, что бы мой ИК передатчик можно было использовать отдельно от ПК, т.е. он должен быть с кнопками и батарейкой, но потом решил пульт с кнопкаи оставить на потом. Я хотел, что бы передатчиком можно было управлять через USB-UART и чтобы он понимал просто текстовые команды, тогда бы его можно было подключать даже к роутеру.
Схема получилась довольно простой:
Компоненты:
- FTDI FT232RL
- Atmel ATtiny2313-20SU в корпусе SOIC20
- Кварц на 7,3728 МГц
- 4 конденсатора 0.1 на мкФ SMD0805
- 2 конденсатора на 20пФ
- 1 танталовый конденсатор на 10 мкФ
- 1 SMD-светодиод (типоразмер 1206) для индикации работы FT232 (не обязательно)
- 1 SMD резистор на 560 Ом для светодиода (опять же по желанию)
- 1 ИК-светодиод, я использовал L-34F3C
- 1 SMD резистор на 100 Ом для ИК светодиода.
Сигнал на ИК диод подается через 5 выводов МК, конечно, правильней было бы управлять транзистором, но я захотел сделать схему как можно проще. При выборе резистора R2 нужно помнить, что максимальный ток для ATtiny2313 составляет 200 мА на все выводы, а на 1 вывод 40 мА.
Информационный светодиод подключен к выводу CBUS3 микросхемы FT232, по умолчанию на него выводится сигнал PWREN#, т.е. светодиод горит, когда FT232 подключена к ПК и инициализировалась. Этому выводу можно назначить и другую функцию, например, RXTXLED# — тогда светодиод будет моргать при передачи данных. Сделать это можно с помощью утилиты FT_PROG. Утилит для Linux, которые умеют менять назначение выводов я что-то не нашел.
Т.к. дома я использую ОС Ubuntu, я решил разводить плату в KiCAD (это конечно не Altium Designer, но со своей задачей он справляется), все компоненты я использовал из стандартных библиотек:
Архив с KiCAD проектом: Remote_USB_PCB.zip
Плата односторонняя, травил методом ЛУТ-а.
Стоит заметить, что я специально выбрал контроллер в SOIC корпусе, т.к. его проще расположить на плате да и травить/паять тоже проще. А вот расстояние между ножками FT232RL довольно маленькое, поэтому после переноса тонера на плату, перед травлением, нужно очистить расстояние между выводами от остатков бумаги каким-нибудь острым предметом. Я поленился и не сделал этого, в итоге пришлось в некоторых местах подрезать площадки, а то они слились.
Из-за моего желания простоты, изначально плата была без кварца, и выглядела так:
Но, видимо, стабильности внутреннего RC-генератора не хватало и Roomba никак не реагировал на команды, хотя осциллограммы нескольких периодов показывали, что всё нормально. В итоге я примастил кварц на обрезке ненужной платы сверху:
Программирование
Программа написана на C (AVR-GCC), писал в основном в CodeBloks, но разок пришлось отладить в Atmel Studio. На «правильность» и «красивость» код совсем не претендует, т.ч. прошу это учесть и сильно не критиковать (но полезные указания приветствуются).
/*
Управление роботом-пылесосом 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:C S D P W L R F < >";
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;
}
}
}
Архив с проектом: Roomba_Remote_USB.zip | Отдельно hex-файл
Прошивал программатором USBasp с помощью утилиты avrdude.
Для прошивки Fuse-битов нужно запустить с параметрами:
avrdude -p t2313 -c usbasp -U lfuse:w:0xEC:m
Для прошивки hex-файла:
avrdude -p t2313 -c usbasp -U flash:w:./bin/Debug/Roomba_Remote.elf.hex
Для отправки команды нужно набрать соответствующий символ в терминале:
Команда | Символ |
---|---|
LEFT | L |
FORWARD | F |
RIGHT | R |
SPOT | S |
DOCK | D |
CLEAN | C |
PAUSE | P |
POWER | W |
FORWARD_LEFT | < |
FORWARD_RIGHT | > |
Есть ещё одна недокументированная функция — см. листинг. ;-)
На правильную команду контроллер отвечает переводом строки, на неправильную краткой справкой.
Небольшой ролик с демонстрацией работы:
Сейчас меня не устраивает дальность работы: нужно направлять светодиод точно на Roomb-у, а это не очень удобно. В связи с чем я подумываю заменить резистор R2 на резистор с меньшим номиналом, например 68 Ом, или же поменять ИК светодиод.
Также подумываю об обычном пульте с кнопками, в связи с чем есть опрос ниже.
P.S.
Об орфографических ошибках и ошибках оформления прошу сообщать через ЛС.
Автор: lohmat