Управляем машинкой через Bluetooth с планшета или телефона под Android

от автора

Привет, Хабр!

Сегодня я расскажу о том, до чего может дойти программист с кризисом среднего возраста в попытках хоть как то компенсировать недостаток моднейших игрушек в далеком и полузабытом детстве.

В радиоуправляемые вертолеты я с сыновьями уже поиграл, хорошо, но в стандартной московской квартире немного тесновато, а на улице холодно, грязно и ветер. Придется играть в машинки, благо, под рукой есть вот такой вот аппарат:

Дети его уже украсили граффити и немного пообтерли, но оно все еще ездит, если пульт найти… А вот пульт куда то потерялся. Ну, зато есть планшет с Bluetooth! Осталось как то их подружить вместе. Через Google ищется много таких проектов, поэтому усложним задание — сделаем пропорциональное рулевое управление и плавную регулировку скорости!

Нам понадобятся:

Безжалостно выкидываем мотор постоянного тока, который дергал рулевую тягу, вместо него надфилем, монтажным пистолетом и другими народными методами вставляем сервопривод. Собираем на макетке простейшую схему с Ардуиной и остальными компонентами. Схему подключений приводить не буду, просто цеплял все на первые попавшиеся ноги ардуины, с какой то итерации даже заработало. После нескольких попыток скомпоновать все так, чтобы поместилось на родное шасси получилось так:

Bluetooth модуль спрятан с нижней стороны макетки, dc converter потом прилепил сверху на заднюю часть шасси:

Осталось залить скетч, так как Bluetooth адаптер у нас висит на том же serial порту, что и USB TTL адаптер, перед тем, как залить скетч, не забываем сдернуть RX/TX с Bluetooth модуля. Кстати, была идея использовать SoftwareSerial для него, но пришлось ее отбросить, так как SoftwareSerial дает странные спонтанные подергивания на сервоприводе.

#include <Servo.h>  Servo servoSteering; const int servoSignalPin = 7; // servo yellow(signal) pin  const int servoMiddle = 88; // servo middle position const int servoMaxAngle = 35; // servo max angle (degrees) const int servoLeftBound = servoMiddle - servoMaxAngle; const int servoRightBound = servoMiddle + servoMaxAngle;  int servoPos = servoMiddle; int servoTrim = 0;  const int dcMotorA1Pin = 11; // dc motor A+ pin const int dcMotorA2Pin = 12; // dc motor A- pin const int dcMotorPWMPin = 3; // dc motor PWM pin  const int ledPin = 13; // data received pin  const int dcMotorPWMLowerBound = 128; // dc motor min PWM (min speed) const int dcMotorPWMHighBound = 255; // dc motor max PWM (max speed)  int dcMotorDir = 1; // 1 - forward, -1 - backward int dcMotorSpd = 0; // 0 - stand still, value between dcMotorPWMLowerBound and dcMotorPWMHighBound to move  const int cmdTimeout = 2000; // max time between commands from rc control, will stop if no new commands arrive  typedef enum { NONE = 0, SERVO_L, SERVO_R, TRIM, DCMOTOR_F, DCMOTOR_B, STOP } mode_t; mode_t opMode = NONE;  #define IS_DIGIT(x) ('9' >= (x) && '0' <= (x))  unsigned long lastCmd = 0;  void setup() {    Serial.begin(9600);    servoSteering.attach(servoSignalPin);    pinMode(dcMotorA1Pin, OUTPUT);   pinMode(dcMotorA2Pin, OUTPUT);   pinMode(dcMotorPWMPin, OUTPUT);    pinMode(ledPin, OUTPUT);    digitalWrite(dcMotorA1Pin, 0);   digitalWrite(dcMotorA2Pin, 0);   analogWrite(dcMotorPWMPin, 0); }  void loop() {    while (Serial.available()) {      digitalWrite(ledPin, HIGH);      int ch = Serial.read();     lastCmd = millis(); // save command receive time for later      if (opMode == NONE) {       switch (ch) {         case 'T':         case 't': // trim mode           opMode = TRIM;           break;         case 'L':         case 'l': // servo left mode           opMode = SERVO_L;           break;         case 'R':         case 'r': // servo right mode           opMode = SERVO_R;           break;         case 'F':         case 'f': // dc motor move front mode           opMode = DCMOTOR_F;           break;         case 'B':         case 'b': // dc motor move back mode           opMode = DCMOTOR_B;           break;         case 'S':         case 's': // full stop mode           servoPos = servoMiddle + servoTrim; // center front wheels           dcMotorDir = 1; // 1st gear           dcMotorSpd = 0; // neutral on         default:           opMode = NONE;           break;       }     } else if (IS_DIGIT(ch)) {       switch (opMode) {         case TRIM:           servoTrim = '5' - ch;           break;         case SERVO_L:           servoPos = servoMiddle + servoTrim - (ch - '0') * servoMaxAngle / 10;           if (servoPos < servoLeftBound) servoPos = servoLeftBound;           break;         case SERVO_R:           servoPos = servoMiddle + servoTrim + (ch - '0') * servoMaxAngle / 10;           if (servoPos > servoRightBound) servoPos = servoRightBound;           break;         case DCMOTOR_F:           dcMotorDir = 1;           dcMotorSpd = ch == '0' ? 0 : dcMotorPWMLowerBound + (dcMotorPWMHighBound - dcMotorPWMLowerBound) * (ch - '0') / 10;           break;         case DCMOTOR_B:           dcMotorDir = -1;           dcMotorSpd = ch == '0' ? 0 : dcMotorPWMLowerBound + (dcMotorPWMHighBound - dcMotorPWMLowerBound) * (ch - '0') / 10;           break;         default:           break;       }              opMode = NONE;     }      if (dcMotorDir == 1)       Serial.print("F ");     else       Serial.print("R ");      Serial.print(dcMotorSpd);     Serial.print(" S ");     Serial.print(servoPos);     Serial.print(" T ");     Serial.println(servoTrim);      digitalWrite(dcMotorA1Pin, dcMotorDir == 1 ? 1 : 0);     digitalWrite(dcMotorA2Pin, dcMotorDir == 1 ? 0 : 1);     analogWrite(dcMotorPWMPin, dcMotorSpd);      servoSteering.write(servoPos);   }      if (dcMotorSpd > 0 && (lastCmd + cmdTimeout) < millis()) {      // rc control doesn't send anything, out of range or hang up, do full stop     dcMotorDir = 1; // 1st gear     dcMotorSpd = 0; // neutral on      digitalWrite(dcMotorA1Pin, 1);     digitalWrite(dcMotorA2Pin, 0);     analogWrite(dcMotorPWMPin, 0);      servoSteering.write(servoPos);   }    digitalWrite(ledPin, LOW); } 

Протестировал через PuTTY с ноутбука послылая команды в подцепленное Bluetooth сериал устройство, вроде работает. Осталось соорудить приложение под Android. Надергал в сети картинок, примеров кода, попытался как то склеить, вроде работает:

Попутно вылез глюк с реализацией Bluetooth на некоторых ICS устройствах:
code.google.com/p/android/issues/detail?id=34161

У меня проявилось на Lenovo A800, проявляется в том, что открытый Bluetooth сокет сразу же закрывается. Лечить, видимо, только сменой прошивки девайса, все опробованные обходные пути не заработали…

Ну и на закуску код Android приложения:
github.com/robotsrulz/BluetoothRC

И короткое видео, где сначала сын пытается водить машину, но получается только по прямой, а потом я пытаюсь одной рукой рулить, а другой продолжать съемку:
plus.google.com/u/0/photos?pid=5988360256454694594&pids&oid=106468370342958692108

ссылка на оригинал статьи http://habrahabr.ru/post/216851/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *