6 января 2012

Радиоуправляемая Wi-Fi машинка с камерой

DIY или Сделай сам
Из песочницы

Начало


Летом появилась мысль сделать радиоуправляемую машинку, но не просто нечто похожее на творение китайской инженерной мысли, которое продается на каждом шагу, а машинку, управлять которой можно было бы с компьютера или телефона. Понято, что машинка, которой можно управлять через Wi-Fi в чистом виде, совершенно не интересна. А вот если бы у нее была бы камера? А если еще и управление через 3G/EDGE/GPRS? Вот это – другое дело! Значит, управляющее устройство должно иметь USB и Wi-Fi (ну или только USB, можно купить USB Wi-Fi адаптер). Теперь нужно придумать, как управлять двигателями. Изначально я хотел сделать это с помощью COM-порта и регистра сдвига (74HC595), но спалив 5 таких микросхем, от такого способа отказался. Позже мой взгляд пал на Arduino, а именно на Arduino Duemilanove. Эта плата имеет 14 цифровых I/O портов, из них шесть – ШИМ (можно будет управлять напряжением на двигателе и повесить сервы для камеры), два можно использовать как Tx и Rx последовательного порта.

Роутер


Обнаружив в своем городе роутер D-Link DIR-320, у которого есть USB порт, сразу же его купил. Придя домой, узнал, что у этого роутера есть невыведенный UART-порт. Таким образом, у нас появляется канал связи между роутером и ардуиной.
Для роутера я выбрал прошивку OpenWrt. Можно скачать готовую прошивку с OpenWrt для DIR-320 это здесь. Уже не помню почему, но я решил собрать прошивку сам (подробно описано тут). Для этого понадобится Linux (я собирал на Ubuntu 11.10). Для начала, скачаем исходники прошивки и соберем все, что нужно:
svn co svn://svn.openwrt.org/openwrt/branches/backfire dir320
cd dir320
./scripts/feeds update -a && ./scripts/feeds install –a
make prereq && make tools/install && make toolchain/install

Конфигурирование прошивки
make menuconfig

Выбираем следующие пакеты:
  • Target System ---> <*> Broadcom BCM947xx/953xx – ядро 2.6
  • Image configuration ---> <*> LAN IP Address ---> <IP-адрес> – [не обязательно] Можно выбрать IP-адрес, который будет у роутера после загрузки ядра и всех модулей
  • Kernel modules ---> <*> Filesystems ---> <*> kmod-fs-ext3 — Об этом позже
  • Utilities ---> Filesystem ---> <*> e2fsprogs – И об этом
  • Utilities ---> disc ---> <*> block-extroot – И об этом тоже
  • Kernel modules ---> <*> USB Support ---> <*> kmod-usb-core – поддержка USB
  • Kernel modules ---> <*> USB Support ---> <*> kmod-usb-ohci – для USB-хаба. Зачем он? Об этом тоже позже
  • Kernel modules ---> <*> USB Support ---> <*> kmod-usb-storage – поддержка USB-флешек
  • Kernel modules ---> <*> USB Support ---> <*> kmod-usb2 – USB 2.0
  • Administration --> webif ---> <*> webif-applications – админка
  • Kernel modules ---> <*> Video Support ---> <*> kmod-usb-video-core – поддержка USB-video
  • Kernel modules ---> <*> Video Support ---> <*> kmod-usb-video-uvc – поддержка UVC-веб камер

Последний пункт выбирайте сами, у меня была UVC веб камера.
Так зачем же мы выбрали те пакеты, назначение которых я не объяснил? Проблема в том, что объём флэш-памяти установленной в роутере — 4МБ, что может помешать дальнейшей нашей работе. Мы же перенесем rootfs на флешку, и роутер будет грузиться с нее.
Кстати, про флэш-память: нужно не забыть следующее:
make kernel_menuconfig
  • Device Drivers ---> <*> Memory Technology Device (MTD) support ---> RAM/ROM/Flash chip drivers ---> [*] Flash chip driver advanced configuration options --> [*] Specific CFI Flash geometry selection --> [*] Support 8-bit buswidth
  • Device Drivers ---> <*> Memory Technology Device (MTD) support ---> RAM/ROM/Flash chip drivers ---> [*] Flash chip driver advanced configuration options --> [*] Specific CFI Flash geometry selection --> [*] Support 16-bit buswidth

И еще в Kernel Hacking’е нужно исправить console=/dev/ttyS0 на console=/dev/null, чтоб роутер не использовал этот порт как отладочный.
Компилируем и прошиваем

Компиляция прошивки:
make V=99 -j2

Теперь нужно ее прошить:
Для bash’а:
#!/bin/bash
echo "=================================================================="
echo "This script will upload dd-wrt firmware (firmware.bin)"
echo "in the current directory to 192.168.0.1 "
echo "during the router's bootup. "
echo ""
echo "* Set your ethernet card's settings to: "
echo "   IP:   192.168.0.10 "
echo "   Mask:  255.255.255.0 "
echo "   Gateway: 192.168.0.1 "
echo "* Unplug the router's power cable. "
echo ""
echo "Press Ctrl+C to abort or any other key to continue... " 

read

echo ""
echo "* Re-plug the router's power cable. "
echo "" 
echo "=================================================================="
echo "Waiting for the router... Press Ctrl+C to abort. "
echo ""


try(){ 
ping -c 1 -w 1 192.168.0.1 
} 
try 
while [ "$?" != "0" ] ; 
do 
try 
done 
echo "*** Start Flashing **** " 
atftp --no-source-port-checking -p -l firmware.bin 192.168.0.1 
echo "Firmware successfully loaded!"

Для винды:
@echo off
echo ==================================================================
echo This batch file will upload dd-wrt firmware in the current directory to
echo 192.168.0.1 during the router's bootup.
echo.
echo * Set your ethernet card's settings to:
echo   IP:   192.168.0.2
echo   Mask:  255.255.255.0
echo   Gateway: 192.168.0.1
echo * Unplug the router's power cable.
echo.
echo Press Ctrl+C to abort or any other key to continue...
pause > nul
echo.
echo * Re-plug the router's power cable.
echo.
echo ==================================================================
echo Waiting for the router... Press Ctrl+C to abort.
echo.
set FIND=%WINDIR%\command\find.exe
if exist %FIND% goto LPING
set FIND=%WINDIR%\system32\find.exe
if exist %FIND% goto LPING
set FIND=find
:LPING
ping -n 1 -w 50 192.168.0.1 | %FIND% "TTL="
if errorlevel 1 goto LPING
echo *** Start Flashing ****
tftp -i 192.168.0.1 put firmware.bin
if errorlevel 1 goto LPING
set FIND=
echo.
echo ==================================================================
echo * WAIT for about 2 minutes while the firmware is being flashed.
echo * Reset your ethernet card's settings back to DHCP.
echo * The default router address will be at 192.168.1.1
echo.
Pause

Настройка загрузки с флешки

После первого включения заходим на веб-интерфейс роутера и изменяем пароль. Теперь подключаемся к нему через SSH. Нужно настроить загрузку с флешки, для этого сначала нужно ее разметить. У меня было два раздела: первый – ext3-раздел для rootfs, второй – swap. Открываем /etc/config/fstab в vim’е и пишем то, что соответствует нашей фелшке. У меня так:
 config global automount
    option from_fstab 1
    option anon_mount 1

config global autoswap
    option from_fstab 1
    option anon_swap 0

config mount
    option target  /
    option device  /dev/sda1
    option fstype  ext3
    option options rw,sync
    option enabled 1
    option enabled_fsck 1
    option is_rootfs 1

config swap
    option device  /dev/sda2
    option enabled 1

Сохраняем, перезагружаемся (reboot).
Демон

Управлять двигателями будет ардуина, поэтому напишем демон, который будет перенаправлять всё, что пришло на TCP порт 5554 в /dev/ttyS0.
Мой скомпилированный вариант демона искать в архиве (card)
Компилируем с помощью gcc, который был собран в процессе подготовки к сборке прошивки:
<…>/dir320/staging_dir/toolchain-mipsel_gcc-4.3.3+cs_uClibc-0.9.30.1/usr/bin/mipsel-openwrt-linux-uclibc-gcc-4.3.3 <имя файла с исходниками> -o <имя выходного файла>

Небольшое отступление об удобстве организации работы с роутером

  • После каждого включения мне приходилось писать opkg update, поэтому я его добавил в /etc/rc.local
  • Довольно удобно использовать FTP-сервер. Я поставил pure-ftpd. Для этого пишем:
    opkg install pure-ftpd

    Добавим его в /etc/rc.local:
    pure-ftpd -4 –B –M –l unix –U 000:000
  • Удобно будет сменить веб-интерфейс на luci, для этого пишем:
    opkg remove webif*
    	opkg install luci

Демон [продолжение]

Заливаем на роутер наш демон, добавляем его в автозагрузку.
Теперь ставим mjpg-streamer:
opkg install mjpg-streamer

Пишем в /etc/config/mjpg-stramer следующие:
config mjpg-streamer
	option device “/dev/video0” 
	option resolution “640x480”
	option fps “24” 
	option port “8080”
	option enabled “true”

Пробуем подключить камеру. Если все нормально, то можно будет увидеть изображение тут:
http://<IP-адрес_роутера>/?action=stream.

Arduino и соединение


Для начала определимся со схемой подключения двигателей. Так как я брал корпус от уже готовой машинки, то мне с двигателями повезло – они уже там были. Передний отвечал за повороты (влево, вправо, прямо), а задний за движение (мне пришлось его поменять на двигатель кнопки блокировки дверей какого-то ВАЗа). Управлять нагрузками можно ардуиной с помощью полевых транзисторов (95N2LH5, но я использовал IRF 630, потому что и эти ели нашел в своём городе). Подключение такое: земля транзистора – к управляющему пину ардуины, source – к земле ардуины и минусу питания нагрузки, drain – к минусу нагрузки, плюс питания к плюсу нагрузки. Но таким образом мы сможем ездить только вперед и поворачивать только в одну сторону. Для того чтобы справиться с проблемой, к нам на помощь спешит реле с двумя группами контактов. У меня один двигатель (передний) питался 6 вольтами, а другой 12. При этом использовалось два 6 вольтовых аккумулятора (один из них — свинцово-кислотный от бесперебойника), учитывая, что минус роутера позже придется соединить с землей ардуины, то получить 6 вольт для роутера не получается (проверяйте сколько вольт подаёте на роутер — мне пришлось покупать еще один после того, как я подал на него 12 вольт). Поэтому пришлось использовать еще одну релюшку для подачи/неподачи питания на передний двигатель.
Схема
Схему рисовал давно. Теперь там все транзисторы полевые и нет резисторов.
Теперь о самом коде. У меня все довольно просто – есть 4 команды, у которых есть свой параметр размером 1 байт:
  • m – Отвечает за напряжение, а, следовательно, и за скорость, на двигателе значение от 0 до 255
  • r – Отвечает за повороты. “1” – поворачивать, “0” – не поворачивать
  • n – “1” – ехать назад, “0” – ехать вперед
  • e – “1” – поворачивать в другую сторону

Вот мой код программы для ардуины:
int inByte, val;

void setup() {     
 Serial.begin(9600);
 pinMode(2, OUTPUT);
 pinMode(4, OUTPUT);
 pinMode(7, OUTPUT);
}

void loop() {
 if (Serial.available() > 0)
 {
  inByte = Serial.read();
  if ((inByte=='n')||(inByte=='e')){
   while (Serial.available()==0) {}
   val=Serial.read();
   if (inByte=='n'){
    if (val=='1'){
     digitalWrite(2, HIGH);
     Serial.print("Writing to 2 pin\n\r");
    }
    if (val=='0'){
     digitalWrite(2, 0);
     Serial.print("Writing to 2 pin\n\r");
    }
   } 
   if (inByte=='e'){
    if (val=='1'){
     digitalWrite(4, HIGH);
     Serial.print("Writing to 4 pin\n\r");
    }
    if (val=='0'){
     digitalWrite(4, LOW);
     Serial.print("Writing to 4 pin\n\r");
    }
   }
  }
  if ((inByte=='m')||(inByte=='r')){
   while (Serial.available()==0) {}
   val=Serial.read();
   if (inByte=='m'){
    if (val!='0')
     analogWrite(3, val);
    else
     analogWrite(3, 0);
    Serial.print("Writing to 3 pin\n\r");
   }
   if (inByte=='r'){
    if (val=='1'){
     digitalWrite(7, HIGH);
     Serial.print("Writing to 7 pin\n\r");
    }
    if (val=='0'){
     digitalWrite(7, LOW);
     Serial.print("Writing to 7 pin\n\r");
    }
   }
  }
 }
}

Как видно, задний двигатель у меня подключен к 3 пину, передний – к 7, реле заднего – к 2 пину, переднего – к 4. Так как 3 – это ШИМ-пин, то используя analogWrite(3, val);, где val от 0 до 255, мы можем управлять напряжением на двигателе.
Разбираем наш маршрутизатор. Видим UART порт. Соединяем его с ардуиной.
UART на роутере
Теперь смотрим, как это все работает. Подключаемся телнетом к нашему порту и проверяем:
  • n1 – щелкает реле
  • m<пробел> — колёса начинают немного вращаться
  • n0 – колеса вращаются в другую сторону
  • m0 – колеса перестают вращаться
  • r1 – поворачивают передние колеса
  • e1 – колеса поворачивают в другую сторону
  • r0 – колеса становятся прямо
  • e0 – щелкает реле

Для отладки работы с ком портом на роутере можно использовать minicom (opkg install minicom).

Программная часть


В архиве моя программка для управления машинкой (rotate и power из архива нужно скопировать в /bin/ на роутере, card – мой демон). Работает только с джойстиком. На вкладке планирование вы можете написать bash скрипт (не забудьте opkg install bash на роутере) для его выполнения с помощью демона cron. Так как этот демон нужно после изменения его настроек перезапускать, моя программа запускает скрипт по адресу http://<IP-адрес>/cron-restart. Поэтому нужно его создать (/www/cgi-bin/cron-restart) и не забыть сделать исполняемым. Код:
#!/bin/bash
/etc/init.d/cron stop; /etc/init.d/cron start

Заключение


Вроде бэ ничего не забыл. А, ну, конечно же! Вот мой шедевр:


Фотка машинки

Фотка машинки

Фотка машинки

Фотка машинки

Фотка машинки

К такому девайсу можно присоединить Bluetooth (не пробовал, но драйвера есть), 3G-модем (интернет получить у меня получилось, но похоже провайдер не выдаёт каждому клиенту собственный внешний ip-адрес, поэтому придется использовать что-то типа back-connect’а или vpn), gps приёмник (проблем возникнуть не должно – ведь он должен определиться как последовательный порт).

Примечания


Если вдруг роутер перезагружается, то стоит убрать от него подальше все провода или все их экранировать. Экспериментальным путем я понял, что роутер может перезагрузиться от наводок, поэтому пришлось обмотать хаб несколькими слоями изоляции и алюминиевой фольги.
И вот еще. Вместо роутера можно использовать Raspberry Pi, а вместо транзисторов и реле — Arduino Motor Shield.

Архив, о котором не раз я упоминал (зеркало).
Теги:Wi-Filinuxмашинкаrcdiyarduino
Хабы: DIY или Сделай сам
+149
60,7k 369
Комментарии 62
Похожие публикации
Administrator Linux. Professional
18 января 202180 000 ₽OTUS
Безопасность Linux
12 февраля 202130 000 ₽OTUS
Data Analyst
8 декабря 2020102 000 ₽SkillFactory
Python для анализа данных
9 декабря 202024 900 ₽SkillFactory
Профессия Data Scientist
9 декабря 2020162 000 ₽SkillFactory
Лучшие публикации за сутки