Управляем роботом-пылесосом iRobot Roomba через ИК

в 19:31, , рубрики: diy или сделай сам, irobot, roomba, программирование микроконтроллеров, робототехника, метки: , ,

Управляем роботом пылесосом iRobot Roomba через ИК
Перед новым годом у меня появился полезный питомец — 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.

Также автор приводит коды найденных им команд

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

С указанными интервалами у меня не заработало и я пошёл другим путём: на том же форуме выкладывались коды для irshell для управления с PSP.

Вот они

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

Здесь команды записаны кодами в шестнадцатеричной системе, это так называемый Pronto IR формат. Я нашёл неплохое описание (Яндекс.Диск — на всякий случай) их запись оказалась довольно простой.

Некоторые иллюстрации из описания формата

Управляем роботом пылесосом iRobot Roomba через ИК
Управляем роботом пылесосом iRobot Roomba через ИК

Рассмотрим на примере команды 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 мкс.
Далее — по аналогии. Получается, сигнал должен иметь такой вид:
Управляем роботом пылесосом iRobot Roomba через ИК
Если первая пачка — это единица, а вторая нуль, то коды команд из сообщения с форума верны:

Команда Код
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 и чтобы он понимал просто текстовые команды, тогда бы его можно было подключать даже к роутеру.

Схема получилась довольно простой:

Управляем роботом пылесосом iRobot Roomba через ИК

Компоненты:

  • 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, но со своей задачей он справляется), все компоненты я использовал из стандартных библиотек:

Управляем роботом пылесосом iRobot Roomba через ИК

Архив с KiCAD проектом: Remote_USB_PCB.zip
Плата односторонняя, травил методом ЛУТ-а.
Стоит заметить, что я специально выбрал контроллер в SOIC корпусе, т.к. его проще расположить на плате да и травить/паять тоже проще. А вот расстояние между ножками FT232RL довольно маленькое, поэтому после переноса тонера на плату, перед травлением, нужно очистить расстояние между выводами от остатков бумаги каким-нибудь острым предметом. Я поленился и не сделал этого, в итоге пришлось в некоторых местах подрезать площадки, а то они слились.

Из-за моего желания простоты, изначально плата была без кварца, и выглядела так:
Управляем роботом пылесосом iRobot Roomba через ИК
Но, видимо, стабильности внутреннего RC-генератора не хватало и Roomba никак не реагировал на команды, хотя осциллограммы нескольких периодов показывали, что всё нормально. В итоге я примастил кварц на обрезке ненужной платы сверху:
Управляем роботом пылесосом iRobot 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.
Об орфографических ошибках и ошибках оформления прошу сообщать через ЛС.

Когда соберу простой пульт с кнопками нужно ли про него писать статью?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Проголосовало 10 человек. Воздержалось 9 человек.

Автор: lohmat

Источник

Поделиться

* - обязательные к заполнению поля