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