...

понедельник, 24 марта 2014 г.

[Из песочницы] Управляем машинкой через 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 устройствах:

http://ift.tt/OQveYO


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


Ну и на закуску код Android приложения:

http://ift.tt/1rlZREI


И короткое видео, где сначала сын пытается водить машину, но получается только по прямой, а потом я пытаюсь одной рукой рулить, а другой продолжать съемку:

http://ift.tt/1rlZTMS


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий