arduinoBTcontrol — управление Arduino через Bluetooth с Android

Обновлено 06.08.2016

Управление Arduino через Bluetooth с Android с помощью App Inventor 2

Вот пример управления Arduino через Bluetooth с использованием Bluetooth-модуля HC-06 и Android-приложения. В примере используется Arduino Nano, но другие Arduino тоже подойдут. Вместо HC-06 можно использовать модуль HC-05.

arduinoBTcontrol — макетная плата

Android-приложение создано в App Inventor, файл aia можно скачать внизу страницы.

Приложение имеет:

  • Кнопки для включения и выключения светодиодов

  • Слайдеры для управления RGB-светодиодом

  • Навигационную клавиатуру

  • Командные кнопки

Экраны приложения

Приложение имеет демо-режим, в котором кнопки работают, но команды не отправляются через Bluetooth.

Настройка Arduino

Подключение светодиодов

Схема подключения светодиодов

Автор использовал RGB-светодиод с общим анодом. Если вы используете светодиод с общим катодом, общий пин нужно подключить к земле (GND) вместо VCC, и слайдеры будут работать в обратном направлении.

Использованы резисторы 220 Ом и 330 Ом (то, что было под рукой), но подойдёт любой резистор от 220 Ом до примерно 480 Ом. Просто учтите, что чем выше номинал резистора, тем тусклее будет светодиод.

Подключение Bluetooth-модуля

Подключение Bluetooth-модуля
  • Arduino D2 (программный RX) к BT TX

  • Arduino D3 (программный TX) к BT RX через делитель напряжения

  • GND к GND

  • BT VCC к 5V выходу Arduino

Хотя BT-модуль может принимать входное напряжение от 3.6 до 6В, пины данных рассчитаны только на 3.3В. Это означает, что нельзя подключать пин Arduino D3 (TX) напрямую к пину RX BT-модуля. Arduino считает 3.3В от пина TX BT-модуля как HIGH, поэтому этот пин можно подключить напрямую. Пин Arduino RX (5В) нужно понизить до 3.3В перед подключением к пину TX BT-модуля — простой способ сделать это — использовать делитель напряжения из 2 резисторов.

BT-модуль потребляет максимум 40 мА, поэтому его безопасно питать от пина 5V Arduino.

Используются настройки Bluetooth-модуля по умолчанию:

  • Скорость = 9600 бод

  • Имя = HC06

Сопряжение

Перед подключением приложения к HC-06 необходимо выполнить сопряжение с Android-устройством.

Включите HC-06. Светодиод будет быстро мигать.

Откройте «Настройки» на Android-устройстве и выберите «Bluetooth».

Если устройство не сканирует автоматически, выполните поиск доступных Bluetooth-устройств вручную. HC-06 должен появиться в списке.

Выберите HC-06. Потребуется ввести PIN-код. PIN-код по умолчанию — 1234.

Имя модуля HC-06 может включать MAC-адрес — серию шестнадцатеричных чисел.

Сопряжение HC-06 с Android

После сопряжения HC-06 можно подключиться к нему из приложения arduinoBTcontrol.

Подключение к Arduino

Запустите Arduino — светодиод на Bluetooth-модуле должен быстро мигать.

Откройте приложение arduinoBTcontrol на Android-устройстве.

Перейдите на экран настроек.

Нажмите кнопку Bluetooth — появится список сопряжённых устройств.

Выберите HC-06.

После подключения Arduino кнопка Bluetooth изменит текст на «Connected», а светодиод на Bluetooth-модуле перестанет мигать и загорится постоянно.

Подключение к Arduino

Управление Arduino

После подключения вернитесь на экран MAIN и нажимайте кнопки или перемещайте слайдеры.

Кнопки LED переключаются между включением и выключением. Первое нажатие включит светодиод, второе — выключит.

Управление LED

Слайдеры передают своё значение сразу при перемещении. Хотя их можно перемещать до подключения Arduino, данные не будут отправляться до установления соединения.

Красный слайдер Синий слайдер

Экран NAV содержит навигационную клавиатуру и 3 командные кнопки.

Кнопки направления на клавиатуре активны, пока они нажаты. Кнопка OK отправляет сигнал сразу при нажатии (не ждёт, пока пользователь уберёт палец). Командные кнопки отправляют уникальные команды и используют обычные нажатия.

Командные кнопки запускают одну из 3 последовательностей на Arduino. При нажатии командной кнопки она становится красной и неактивной до завершения последовательности. Когда последовательность завершена, кнопка возвращается к зелёному цвету и воспроизводится звук готовности. Командные кнопки нельзя нажимать, пока последовательность активна. Для 3 командных кнопок Arduino отправляет сигнал завершения/подтверждения обратно в Android-приложение. Именно этот сигнал запускает сброс командной кнопки.

Команда 3 циклически переключает RGB-светодиод между 3 различными цветами.

Команда 3 — цикл RGB

Скетч Arduino

Android-приложение отправляет ASCII-коды, заключённые в начальные и конечные теги, на Arduino. Arduino получает данные через последовательное соединение с BT-модулем.

Скетч и приложение можно скачать внизу страницы. Комментарии в скетче объясняют, что происходит.

Основные части — функции recvWithStartEndMarkers() и parseData(). Функция recvWithStartEndMarkers() проверяет полученные данные на наличие начального маркера (символ [), и при обнаружении начинает копировать полученные данные в буфер receivedChars[] до нахождения конечного маркера (символ ]).

Функция parseData() проверяет буфер receivedChars[] на наличие команд и при обнаружении выполняет определённое действие, например, включает или выключает светодиод.

/*
* arduinoBTcontrolUpdated
* Bluetooth remote control from an Android app
* Download the Android app from www.martyncurrey.com
*
* Written using IDE 1.6.3
*
* Some of the below code is not very elegant but it should
* be easy to follow what it does.
*
* Pins
* 2 Software serial - RX
* 3 Software serial - TX
* 4 LED
* 5 LED
* 6 LED
* 7 LED
* 8 LED
* 9 RGB LED RED PIN
* 10 RGB LED GREEN PIN
* 11 RGB LED BLUE PIN
* 12 LED
* 14 LED
* 15 LED
* 16 LED
* 17 LED
* 18 LED
*
*/


// DEBUG
// when debug is true, debug messages are sent to the Arduino serial monitor
boolean debug = true;



#include <SoftwareSerial.h>
SoftwareSerial BTserial(2,3); // RX | TX
// BT module is connected to pins D2 and D3.
// Arduino RX (pin D2) to BT TX.
// Arduino TX (pin D3) to BT RX. Needs a voltage divider to bring the voltage down to 3.3V

// max length of received command is 20 chrs not including the \0 character
const byte numChars = 21;
char receivedChars[numChars];
boolean newData = false;

unsigned int red = 255;
unsigned int green = 255;
unsigned int blue = 255;

byte UPpin = 16;
byte DOWNpin = 15;
byte LEFTpin = 14;
byte RIGHTpin = 17;
byte OKpin = 18;


void setup()
{
     pinMode(12, OUTPUT);
     pinMode(8, OUTPUT);
     pinMode(7, OUTPUT);
     pinMode(6, OUTPUT);
     pinMode(5, OUTPUT);
     pinMode(4, OUTPUT);
     pinMode(11, OUTPUT);
     pinMode(10, OUTPUT);
     pinMode(9, OUTPUT);

     // Common anode RGB LED, 255 is off.
     // If using a common cathode RGB LED then remove the next 3 lines.
     analogWrite(9, red);
     analogWrite(10, green);
     analogWrite(11, blue);

     pinMode(14, OUTPUT);
     pinMode(15, OUTPUT);
     pinMode(16, OUTPUT);
     pinMode(17, OUTPUT);
     pinMode(18, OUTPUT);

     Serial.begin(9600);
     if (debug) { Serial.println("arduinoBTcontrol Ver 2.0"); }
     if (debug) { Serial.println("For use with the arduinoBTcontrol Android app"); }
     if (debug) { Serial.println(" "); }

     // The Bluetooth module used has a default baud rate of 9600
     // This can be changed but be aware software serial doesn't like fast baud rates
     BTserial.begin(9600);
}


// All the loop function does is check to see if there is any data from the Bluetooth module
// and if there is see if it is a command
void loop()
{
     if (BTserial.available() > 0)     {  recvWithStartEndMarkers(); }
     if (newData) { parseData(); }
}



void parseData()
{
        newData = false;
        if (debug) { Serial.println( receivedChars ); }


        // BUTTON
        // B001ON - B for button. 001 is the button number. ON = on
        // B001OF - B for button. 001 is the button number. OF = off
        if (receivedChars[0] == 'B'  )
        {
             int tmp = convertToNumber( 1 );
             if ( receivedChars[4] == 'O' && receivedChars[5] == 'N' ) { digitalWrite(tmp,HIGH); }
             if ( receivedChars[4] == 'O' && receivedChars[5] == 'F' ) { digitalWrite(tmp,LOW);  }
        } // BUTTON


        // RGB SLIDER
        // Trrrgggbbb
        if  ( receivedChars[0] == 'T' )
        {
            // For a common anode RGB LED take the value away from 255.
            // If using a common cathode RGB LED then use:
            // red = convertToNumber( 1 );
            // green  = convertToNumber( 4 );
            // blue = convertToNumber( 7 );

            red = 255 - convertToNumber( 1 );
            green  = 255 - convertToNumber( 4 );
            blue = 255 - convertToNumber( 7 );
            analogWrite(9, red);
            analogWrite(10, green);
            analogWrite(11, blue);
        }  // RGB SLIDER



        // NAVIGATION
        // NUON - N = navigation. U for UP. ON for on
        // NUOF - N = navigation. U for UP. OF for off
        if ( receivedChars[0] == 'N' )
        {
              if ( receivedChars[1] == 'U' )   // UP
              {
                  if ( receivedChars[2] == 'O'  && receivedChars[3] == 'N'  ) { digitalWrite(UPpin,HIGH); }
                  if ( receivedChars[2] == 'O'  && receivedChars[3] == 'F'  ) { digitalWrite(UPpin,LOW); }
              }

              if ( receivedChars[1] == 'D' )   // DOWN
              {
                  if ( receivedChars[2] == 'O'  && receivedChars[3] == 'N'  ) { digitalWrite(DOWNpin,HIGH); }
                  if ( receivedChars[2] == 'O'  && receivedChars[3] == 'F'  ) { digitalWrite(DOWNpin,LOW); }
              }

              if ( receivedChars[1] == 'L' )   // LEFT
              {
                  if ( receivedChars[2] == 'O'  && receivedChars[3] == 'N'  ) { digitalWrite(LEFTpin,HIGH); }
                  if ( receivedChars[2] == 'O'  && receivedChars[3] == 'F'  ) { digitalWrite(LEFTpin,LOW); }

              }

              if ( receivedChars[1] == 'R' )   // RIGHT
              {
                  if ( receivedChars[2] == 'O'  && receivedChars[3] == 'N'  ) { digitalWrite(RIGHTpin,HIGH); }
                  if ( receivedChars[2] == 'O'  && receivedChars[3] == 'F'  ) { digitalWrite(RIGHTpin,LOW); }
              }

              if ( receivedChars[1] == 'K' )   // OK
              {
                   if ( receivedChars[2] == 'O'  && receivedChars[3] == 'K'  )
                   {   digitalWrite(OKpin,HIGH);
                       delay(75);
                       digitalWrite(OKpin,LOW);
                   }
              }

        } // NAVIGATION



        // COMMAND
        if (strcmp ("CCMD1",receivedChars) == 0)
        {
                for ( byte Count = 1; Count <= 15; Count++)
                {
                    digitalWrite(4,HIGH); delay(100); digitalWrite(4,LOW);
                    digitalWrite(12,HIGH); delay(100); digitalWrite(12,LOW);
                }
                 sendOK(1);
        }


        if (strcmp ("CCMD2",receivedChars) == 0)
        {
              for ( byte Count = 1; Count <= 5; Count++)
              {
                  digitalWrite(4,HIGH);  delay(100); digitalWrite(4,LOW);
                  digitalWrite(5,HIGH);  delay(100); digitalWrite(5,LOW);
                  digitalWrite(6,HIGH);  delay(100); digitalWrite(6,LOW);
                  digitalWrite(7,HIGH);  delay(100); digitalWrite(7,LOW);
                  digitalWrite(8,HIGH);  delay(100); digitalWrite(8,LOW);
                  digitalWrite(12,HIGH); delay(100); digitalWrite(12,LOW);
              }
              sendOK(2);
        }


        if (strcmp ("CCMD3",receivedChars) == 0)
        {
            red = 255; green = 255; blue = 255;
            for(int red = 255; red >0;    red--)        {  analogWrite(9, red);     delay (5);   }
            for(int red = 0;   red < 256; red++)        {  analogWrite(9, red);     delay (5);   }
            for(int green = 255; green >0;    green--)  {  analogWrite(10, green);  delay (5);   }
            for(int green = 0;   green < 256; green++)  {  analogWrite(10, green);  delay (5);   }
            for(int blue = 255; blue >0;    blue--)     {  analogWrite(11, blue);   delay (5);   }
            for(int blue = 0;   blue < 256; blue++)     {  analogWrite(11, blue);   delay (5);   }
            sendOK(3);
        }

}


// function recvWithStartEndMarkers by Robin2 of the Arduino forums
// See  http://forum.arduino.cc/index.php?topic=288234.0
void recvWithStartEndMarkers()
{
     static boolean recvInProgress = false;
     static byte ndx = 0;
     char startMarker = '[';
     char endMarker = ']';

     char rc;

     if (BTserial.available() > 0)
     {
          rc = BTserial.read();
          if (recvInProgress == true)
          {
               if (rc != endMarker)
               {
                    receivedChars[ndx] = rc;
                    ndx++;
                    if (ndx >= numChars) { ndx = numChars - 1; }
               }
               else
               {
                     receivedChars[ndx] = '\0'; // terminate the string
                     recvInProgress = false;
                     ndx = 0;
                     newData = true;
               }
          }
          else if (rc == startMarker) { recvInProgress = true; }
     }
}



int convertToNumber( byte startPos)
{
    unsigned int tmp = 0;
    tmp = (receivedChars[startPos]-48) * 100;
    tmp = tmp + (receivedChars[startPos+1]-48) * 10;
    tmp = tmp + receivedChars[startPos+2]-48;
    return tmp;
}


// send a simple finished code to the app
void sendOK(int val)
{
    BTserial.print("OK");BTserial.print(val);BTserial.print("#");
    if (debug) { Serial.print("OK");Serial.print(val);Serial.println("#"); }
}

Android-приложение

Android-приложение написано в MIT App Inventor 2, файл aia можно скачать внизу страницы. Вот ключевые части приложения.

При первом запуске приложения вызывается Screen1.Initialize. Он устанавливает значения меток слайдеров и меняет цвета ColorRight слайдеров. Нужные цвета недоступны в дизайнере и устанавливаются в блоках.

Блок Screen1.ErrorOccurred перехватывает системные ошибки и отображает их во всплывающем сообщении.

Screen1.Initialize

Различные экраны — MAIN, NAV и SETTINGS — на самом деле не являются разными экранами. Вкладки — это кнопки, и при нажатии они отображают или скрывают определённые контейнеры. При нажатии вкладки MAIN отображается контейнер MAIN, а контейнеры NAV и SETTINGS скрываются. Этот метод используется для преодоления проблемы с Bluetooth. В Android каждый экран рассматривается как отдельное приложение, и использование традиционных экранов означает, что нельзя использовать Bluetooth-соединение на разных экранах.

Логика очень проста. Автор отключает все вкладки, а затем включает нужную. Код вкладки MAIN вынесен в процедуру, чтобы его можно было вызвать из функции Screen1.Initialize.

Переключение вкладок

Bluetooth-соединение инициируется при нажатии кнопки Bluetooth на экране SETTINGS. При нажатии кнопки приложение проверяет, включён ли Bluetooth, затем доступные сопряжённые устройства копируются в список, проверяется длина списка. Если есть хотя бы одно устройство, открывается ListPicker. Когда пользователь выбирает устройство, вызывается функция BT_LP.AfterPicking.

Если приложение уже подключено, пользователю предлагается закрыть соединение.

Подключение Bluetooth

Если подключение успешно, текст кнопки меняется на CONNECTED и запускается BT_Timer. Если подключение не удалось, отображается сообщение об ошибке.

Обработка подключения

BT_Timer используется для проверки входящих данных. Частота таймера установлена в дизайнере на 200 мс (5 раз в секунду), что достаточно для этого примера. Для более сложных приложений потребуется более быстрый таймер.

BT_Timer

Когда таймер срабатывает, вызывается функция BT_Timer.Timer. Она проверяет наличие новых данных, и если есть — добавляет их в буфер. Буфер хранится в переменной receivedDataFromBT. Затем буфер проверяется на наличие маркера #. Если найден маркер конца данных, вызывается функция updateCMDbtn.

Это базовый пример, и единственная часть, использующая обратную связь от Arduino — 3 кнопки COMMAND на странице NAV. Поэтому простой маркер конца данных вполне приемлем. Для более сложной связи лучше использовать начальные и конечные маркеры.

Обработка данных таймера

Демо-режим позволяет пользователю попробовать приложение без Bluetooth-соединения. В обычном режиме кнопки не работают без соединения, и нажатие кнопки генерирует ошибку «Bluetooth is not connected».

Демо-режим включается и выключается на странице SETTINGS. Кнопка демо-режима переключается между ON и OFF при каждом нажатии.

Демо-режим

Кнопки пинов

На странице MAIN кнопки соответствуют пинам Arduino. Нажатие одной из кнопок переключает её между ON и OFF и отправляет соответствующую команду на Arduino.

Кнопки пинов

При нажатии кнопки её свойство и номер передаются в функцию buttonPressed. Здесь создаётся команда для включения или выключения светодиода, которая передаётся в функцию sendCommand, и фоновое изображение кнопки меняется.

Обработка кнопок пинов

Кнопки навигации отправляют команду при нажатии (touch down) и при отпускании (touch up). По сути, это отправляет сигнал старта и остановки на Arduino.

Кнопка OK работает иначе. Она отправляет команду только при нажатии. Таким образом, Arduino может реагировать сразу при нажатии и не должна ждать, пока пользователь уберёт палец.

Кнопки навигации

Командные кнопки отправляют команду CMD на Arduino, становятся красными и ждут сигнала завершения от Arduino. Во время ожидания командные кнопки неактивны.

Можно заметить, что 3 функции очень похожи и легко могут быть объединены в одну.

Командные кнопки

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

Когда Arduino получает одну из команд CMD и выполняет соответствующую функцию, она отправляет сообщение «я закончил» в формате OK1#, OK2# или OK3#. Когда приложение получает одно из этих сообщений, оно сбрасывает командные кнопки.

Обработка ответов от Arduino

Последние элементы управления — слайдеры. Слайдеры генерируют события при каждом изменении позиции, поэтому могут генерировать много данных. Команда слайдера создаётся объединением значений каждого слайдера в формате Trrrgggbbb. Значения конвертируются в ASCII перед отправкой. Это означает, что формат фиксирован — всегда 3 символа на цвет. Если скорость не критична, использование ASCII для передачи данных очень удобно и надёжно.

Слайдеры RGB

Загрузка

Zip-файл содержит скетч Arduino и файл App Inventor aia.

Скачать arduinoBTcontrolUpdated (1 файл, 273.45 КБ)