Проблемы с модулем Bluetooth(HC 05)
Я пытаюсь создать управляемого по Bluetooth робота 4WD. я следовал учебнику который нашел в Интернете (https://howtomechatronics.com/tutorials/arduino/arduino-robot-car-wireless-control-using-hc-05-bluetooth-nrf24l01-and-hc-12-transceiver-modules/). После успешного подключения моего модуля Bluetooth 2 HC 05 я использовал скрипт из учебника и немного изменил его, чтобы он работал для моей настройки. Код для робота:
//Code for Slave
#define enA 9
#define in1 8
#define in2 7
#define enB 10
#define in3 6
#define in4 5
int xAxis, yAxis;
unsigned int x = 0;
unsigned int y = 0;
int motorSpeedA = 0;
int motorSpeedB = 0;
void setup() {
pinMode(enA, OUTPUT);
pinMode(enB, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);
Serial.begin(9600); // Скорость связи по умолчанию модуля Bluetooth
}
void loop() {
// Значение по умолчанию - нет движения, когда джойстик остается в центре
x = 510 / 4;
y = 510 / 4;
// Считывание входящих данных с джойстика или главного устройства Bluetooth
while (Serial.available() >= 2) {
x = Serial.read();
Serial.print("X: ");
Serial.println(x);
delay(10);
y = Serial.read();
Serial.print("Y: ");
Serial.println(y);
}
delay(10);
// Преобразуйте обратно диапазон 0 - 255 в 0 - 1023, соответствующий приведенному ниже коду управления двигателем
xAxis = x*4;
yAxis = y*4;
// Ось Y используется для прямого и обратного управления
if (yAxis < 470) {
backMov;
// Преобразуйте уменьшающиеся показания оси Y для перехода назад от 470 до 0 в значение от 0 до 255 для ШИМ-сигнала для увеличения частоты вращения двигателя
motorSpeedA = map(yAxis, 470, 0, 0, 255);
motorSpeedB = map(yAxis, 470, 0, 0, 255);
}
else if (yAxis > 550) {
fowardMov;
// Преобразуйте увеличивающиеся показания оси Y для перехода вперед от 550 до 1023 в значение от 0 до 255 для ШИМ-сигнала для увеличения скорости двигателя
motorSpeedA = map(yAxis, 550, 1023, 0, 255);
motorSpeedB = map(yAxis, 550, 1023, 0, 255);
}
// Если джойстик остается посередине, двигатели не двигаются
else {
motorSpeedA = 0;
motorSpeedB = 0;
}
// Ось X используется для левого и правого управления
if (xAxis < 470) {
// Преобразуйте уменьшающиеся показания оси X от 470 до 0 в увеличивающееся значение от 0 до 255
int xMapped = map(xAxis, 470, 0, 0, 255);
// Переместить влево - уменьшить скорость левого двигателя, увеличить скорость правого двигателя
motorSpeedA = motorSpeedA + xMapped;
motorSpeedB = motorSpeedB - xMapped;
// Ограничьте диапазон от 0 до 255
if (motorSpeedA > 255) {
motorSpeedA = 255;
}
if (motorSpeedB < 0) {
motorSpeedB = 0;
}
}
if (xAxis > 550) {
// Преобразуйте увеличивающиеся показания оси X от 550 до 1023 в значение от 0 до 255
int xMapped = map(xAxis, 550, 1023, 0, 255);
// Move right - уменьшить скорость правого двигателя, увеличить скорость левого двигателя
motorSpeedA = motorSpeedA - xMapped;
motorSpeedB = motorSpeedB + xMapped;
// Ограничьте диапазон от 0 до 255
if (motorSpeedA < 0) {
motorSpeedA = 0;
}
if (motorSpeedB > 255) {
motorSpeedB = 255;
}
}
// Предотвратите жужжание на низких скоростях (Отрегулируйте в соответствии с вашими двигателями. Мои двигатели не могли начать движение, если значение PWM было ниже значения 70)
if (motorSpeedA < 70) {
motorSpeedA = 0;
}
if (motorSpeedB < 70) {
motorSpeedB = 0;
}
analogWrite(enA, motorSpeedA); // Отправить ШИМ-сигнал на двигатель A
analogWrite(enB, motorSpeedB); // Отправить ШИМ-сигнал на двигатель B
}
void fowardMov(){
digitalWrite(in2,HIGH);
digitalWrite(in4,HIGH);
digitalWrite(in1, LOW);
digitalWrite(in3, LOW);
}
void backMov(){
digitalWrite(in1,HIGH);
digitalWrite(in3,HIGH);
digitalWrite(in2, LOW);
digitalWrite(in4, LOW);
}
Код для контроллера:
#define Xaxis A0
#define Yaxis A1
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.write(analogRead(Xaxis)/4);
Serial.write(analogRead(Yaxis)/4);
delay(20);
}
После загрузки кода я попытался переместить его, но он не работал, поэтому я поставил операторы печати, чтобы увидеть значение X и Y. изначально он не работал, поэтому я поменял местами модули Bluetooth, которые у меня были, и снова подключил их. я также изменил эту строку.
while (Serial.available() >= 2)
К этому
if (Serial.available() > 0)
я начал получать значения от ведущего устройства к ведомому, но я заметил, что значения были одинаковы для x и y, поэтому я искал другой учебник в Интернете и нашел это (https://create.arduino.cc/projecthub/andriy-baranov/arduino-4wd-rc-car-639953?ref=similar&ref_id=18369&offset=3) читая его код, я задумался, как работает раб. Согласно официальной документации Arduino Serial.read() не принимает никаких аргументов, но я все равно попробовал, и это не сработало. Я решил повторить код из первого учебника, который проверил, на этот раз он выглядел так, как будто работал, поэтому я попробовал переместить джойстик, и последовательный монитор сначала напечатал правильные значения для обоих, а затем поменял их местами, а затем завис. Как я могу остановить его от замораживания и замены значений? Кроме того, почему цикл while не работает вечно, разве объем данных в последовательном буфере всегда не превышает 2? Спасибо.
@Ayanfe Adekanye, 👍2
2 ответа
Ваша настройка слишком сложна! Упростите свою настройку до такой степени, чтобы она касалась только той части, которая вызывает у вас проблемы; в вашем случае связь через последовательный порт между ведущим и ведомым. Решите ТОЛЬКО ЭТУ проблему, а затем двигайтесь дальше
Существует проблема с тем, как передаются и принимаются данные, поскольку предполагается, что передача будет полностью синхронизирована в течение всего срока действия программы. Подумайте, что произойдет, если один байт будет удален из – за ошибки связи-данные x и y будут заменены для дальнейшей передачи.
Первоначальный Отправитель
void loop() {
// put your main code here, to run repeatedly:
Serial.write(analogRead(Xaxis)/4);
Serial.write(analogRead(Yaxis)/4);
delay(20);
}
Оригинальный Приемник
void loop() {
// Значение по умолчанию - нет движения, когда джойстик остается в центре
x = 510 / 4;
y = 510 / 4;
// Считывание входящих данных с джойстика или главного устройства Bluetooth
while (Serial.available() >= 2) {
x = Serial.read();
Serial.print("X: ");
Serial.println(x);
delay(10);
y = Serial.read();
Serial.print("Y: ");
Serial.println(y);
}
delay(10);
. . .
}
Следовательно, должен быть способ синхронизации с первым байтом сообщения. Это может быть достигнуто с помощью пакетов сообщений, в которых начало и конец сообщения разделены уникальными символами, например {
и}
, аналогично формату JSON.
В качестве другого примера, стандарт GPS NMEA использует $
и \n
для разделения сообщений и для разделения полей. В конце также есть контрольная сумма.
Вот неблокирующий алгоритм, который основан на примере readline()
Майенко и примере мигания без задержки для чтения пакета. Функция ReadPacket()
возвращает значение true, когда входящие данные были собраны в допустимый пакет. Светодиод мигает, указывая на то, что микроконтроллер не замерз.
Новый Отправитель
void loop()
{
Serial.print("{"); // Разделитель первого символа.
Serial.print(analogRead(Xaxis)); // Полное разрешение. Не нужно делить на 4.
Serial.print(","); // Разделитель полей.
Serial.print(analogRead(Yaxis)); // Полное разрешение. Не нужно делить на 4.
Serial.print("}"); // Разделитель последних символов.
delay(20);
}
Новый Приемник
void loop()
{
//
// ЗАДАЧА 1: Мигайте без задержки, чтобы указать, что микроконтроллер не завис.
//
const unsigned int INTERVAL = 250;
unsigned long current_timestamp = millis();
static unsigned long previous_timestamp = current_timestamp;
static bool led_state = false;
if (current_timestamp - previous_timestamp >= INTERVAL)
{
led_state = !led_state;
digitalWrite(LED_BUILTIN, led_state);
previous_timestamp += INTERVAL;
}
//
// ЗАДАЧА 2: Чтение и анализ пакета данных.
//
static char packet[100];
const byte NUM_FIELDS = 2;
unsigned int fields[NUM_FIELDS];
static unsigned int x = 512; // Полное разрешение. Не нужно делить на 4.
static unsigned int y = 512; // Полное разрешение. Не нужно делить на 4.
bool new_x = false;
bool new_y = false;
if (ReadPacket(Serial, packet, sizeof(packet)))
{
Serial.print(F("Received packet >"));
Serial.print(packet);
Serial.println(F("<"));
// Проанализируйте пакет для значений x и y в полном разрешении.
const char delimiters[] = "{,}";
char* field;
int i = 0;
field = strtok(packet, delimiters);
while (field != NULL && i < NUM_FIELDS)
{
Serial.print("field = ");
Serial.println(field);
fields[i] = atoi(field);
Serial.print("fields[");
Serial.print(i);
Serial.print("] = ");
Serial.println(fields[i]);
field = strtok(NULL, delimiters);
i++;
}
if (i == NUM_FIELDS)
{
if (x != fields[0])
{
new_x = true;
x = fields[0]; // Полное разрешение. Нет необходимости умножать на 4.
Serial.print("New x = ");
Serial.println(x);
}
if (y != fields[1])
{
new_y = true;
y = fields[1]; // Полное разрешение. Нет необходимости умножать на 4.
Serial.print("New y = ");
Serial.println(y);
}
}
}
Serial.print("x = ");
Serial.println(x);
Serial.print("y = ");
Serial.println(y);
//
// ЗАДАЧА 3: Обработайте значения x и y.
//
if (new_x || new_y)
{
Serial.println("Processing new values.");
. . .
}
}
bool ReadPacket(Stream& stream, char *const packet, const unsigned int SIZE)
{
const char FIRST = '{';
const char LAST = '}';
static bool read_until_last = false;
static unsigned int i = 0;
char ch;
if (stream.available())
{
ch = stream.read();
if (ch == FIRST)
{
Serial.println(F("Received FIRST."));
i = 0;
packet[0] = ch;
read_until_last = true;
return false;
}
if (read_until_last)
{
i++;
if (i > SIZE - 2)
{
Serial.println(F("Buffer overrun. Resetting to look for next packet."));
i = 0;
read_until_last = false;
return false;
}
if (ch == LAST)
{
Serial.println(F("Received LAST."));
packet[i++] = ch;
packet[i] = 0;
read_until_last = false;
return true;
}
else
{
Serial.print(F("Received char >"));
Serial.print(ch);
Serial.println("<");
packet[i] = ch;
return false;
}
}
}
return false;
}
- Bluetooth-модуль HC-05 не принимает AT-команды
- Как прервать соединение HM10 Bluetooth?
- Обеспечиваем более быстрое и точное обнаружение MindWave Mobile
- ПК не может получить доступ к порту HC-06 Bluetooth COM
- Код CRC8 не работает должным образом
- Загрузка Arduino Nano дает ошибку: avrdude: stk500_recv(): programmer is not responding
- Ошибка "'Serial' does not name a type"
- Arduino Pro Micro, получить данные с контакта Tx?
Я упростил свою настройку, удалив все, кроме главного и подчиненного устройств Bluetooth . я сделал простой скетч для отправки данных от ведущего устройства к ведомому. он по-прежнему делает то же самое. он зависает, а затем через некоторое время выводит все данные, отправленные основным устройством. я заметил, что когда я увеличиваю задержку для мастера до 1 секунды, это работает намного лучше, но не приведет ли задержка в 1 секунду к заметной задержке во времени при управлении роботом? Спасибо, @Ayanfe Adekanye
Рад слышать, что вы упростили ситуацию и начинаете получать некоторые обнадеживающие результаты. Если увеличение задержки мастера до 1 секунды работает намного лучше, то вы, возможно, на что-то наткнулись. Попробуйте увеличить задержку еще больше (2 секунды, 10 секунд) и посмотрите, позволит ли это ответу ведущего/ведомого устройства больше походить на "в реальном времени". Если это так, вы можете обнаружить, что скорость передачи данных ведущий/ведомый слишком низкая, и для передачи всех байтов по каналу требуется задержка в 1 секунду, @user3765883
В любом случае, не торопитесь, идите очень медленно, записывайте все и МЕНЯЙТЕ ТОЛЬКО ОДНУ ВЕЩЬ ЗА РАЗ. В какой-то момент вы посмотрите на свои результаты и свои заметки и скажете: "Конечно, почему я не подумал об этом раньше"; -)., @user3765883
Я сделал то, что ты сказал, и кое-что заметил. я запустил задержку мастеров с 2 секунд и начал постепенно уменьшать ее вдвое. На 125 миллисекундах он все еще получал последовательные данные нормально, но иногда с небольшим заметным отставанием. однако отставание длится не более секунды. Я немного изменю код для робота, чтобы ему не нужно было отправлять так много данных. в первом уроке скорость передачи данных, которую он использует, составляет 38400, но я использую 9600. Как вы думаете, это может быть решением моей проблемы? Спасибо, @Ayanfe Adekanye
Старая скорость передачи данных 9600 является древней. Попробуйте 115200 или 230400, @user3765883
Я попытался изменить скорость передачи данных, но это ничего не улучшило, @Ayanfe Adekanye
Если у вас есть "замораживание", на которое не влияет скорость передачи данных, то, возможно, ваш код настраивает, а затем каждый раз демонтирует соединение по циклу, вместо того, чтобы настраивать его один раз, а затем сохранять его открытым в течение всего срока службы программы. Должна быть причина, и тщательное экспериментирование и запись результатов ее выявят., @user3765883
Как вы думаете, проблема у меня с оборудованием? потому что я максимально упростил свой код, отправив номера 1-255 от мастера с циклом for, а затем получив и распечатав полученные данные на последовательном мониторе, но он все равно зависает. используя скорость передачи 23400 бод, я получил только то, что выглядело как случайные числа. я заметил, что если полностью удалить задержку с мастера, она не зависает, хотя некоторые данные теряются., @Ayanfe Adekanye
в общем, аппаратное обеспечение почти никогда не является проблемой, @user3765883