Управляем роботом-пылесосом 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 формат. Я нашёл неплохое описание (Яндекс.Диск — на всякий случай) их запись оказалась довольно простой.
    Некоторые иллюстрации из описания формата



    Рассмотрим на примере команды 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.
    Об орфографических ошибках и ошибках оформления прошу сообщать через ЛС.
    Когда соберу простой пульт с кнопками нужно ли про него писать статью?

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

    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 16
    • +2
      Мне интересно можно ли имея/не имея оригинальный пульт научить обучаемый пульт этим командам. Если да, то DIY DIYом, но для таких криворуких лентяев как я можно будет управлять роботом уже сейчас (точнее после покупки любого обучаемого пульта).
      • +1
        Нужно просто подать эти команды с компьютера и записать на пульт, т.е требуется источник команды которой будет обучаться пульт, так что соберите устройство как в статье и по очереди запишите все команды.
        • +2
          Еще можно, в качестве источника использовать КПК с ИК-портом(или PSP), загрузив Pronto Raw коды, указанные, выше.
      • 0
        Может тогда стоит выдрать из пылесоса фотодиод и вместо него поставить RF-приемник (пара приемник-передатчик у китайцев стоит копьё вообще). А с компа или откуда угодно уже рулить по радиоканалу.

        ЗЫ.
        Почему написал выдрать именно фотодиод — поясню. Я не в курсе как там реализован схематически ИК-канал, так что предполагаю, что всей обработкой рулит непосредственно проц. По сему скорее всего диод подцеплен непосредственно к процессору пылесоса…
        • 0
          Про внутреннее устройство — была статья с разборкой Внутренний мир робота-пылесоса iRobot Roomba 555.
          Фотография платы из статьи
          image

          Если внимательно на нее взглянуть, то можно заметить разъем (типа PS-порта ПК) на который выведен последовательный порт, через него можно всем управлять, даже спецификация есть: roomba_sci_spec_manual.pdf.
          Моя идея состояла в том, чтобы управлять без вмешательства в конструкцию робота.
          • 0
            Видно статью эту я прохлопал как-то. А по поводу не вмешательства — я так и понял.
        • 0
          Возможно в будущем добавят в сам робот возможность писать свои плагины.
          Вот тогда будет интересней.
          • +1
            Уже сейчас через имеющийся в пылесосе порт можно подключить готовую отладочную плату с микроконтроллером (хоть ту же Arduiono), и можно писать все что угодно.
          • 0
            Здорово, молодец, только я бы вместо ИК прицепил какой нибудь RF модуль приёмопередатчика, например Нордик, тогда управлять можно было бы не только в зоне прямой видимости, а даже в соседней комнате.
            • 0
              Можно сделать маленькую коробочку — переходник с радиоинтерфейса или того же голубого зуба на ИК с батарейкой. Прицепил к роботу и управляй как хочешь, хоть с планшета, хоть с компа!
              • +1
                Ага, можно и так, а можно и прямо к разъему румбы подключиться, он под верхней панелью находится. Выше про него писал: тыц.
            • 0
              Спасибо автору. Статья очень интересная и для меня полезная. Купил жене к 8 марта Roombo 620. За пульт с тремя кнопками просили какие то совершенно отмороженные деньги — 1600р, из принципа не стал покупать!
              Благодаря кодам из этой статьи можно достаточно просто прикрутить пылесос к компу. Я хочу сделать радиоудлинитель для управления пылесосом, как только время свободное появится.

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