Search code examples
arduinobluetooth-lowenergyapp-inventor

bluetooth low energy slow reaction time


I am currently trying to use an Adafruit Feather 32u4 to control a 2 motors (a small remote control car) with an android app. Here's what I am using:

MitAppInventor 2 for the app, obviously the Arduino IDE for the car.

App Inventor doesn't have a pleasant way of sharing the code, but basically I get passed the pairing, and get to where its just the buttons to press. They work perfectly, making the car go forwards and backwards, left and right. The problem I have is when I unplug the feather from the computer, the time between button presses and motors moving goes to about 1.5 seconds which is definitely not okay for driving.

All the Arduino does is take in the array from the phone, which will be Status, 0, 1, 2, 3, or 4. Depending on that number it turns the motors on in the desired direction.

The code I used it just modified code from this guide

This is my Arduino IDE code:

/*********************************************************************
 This is an example for our nRF51822 based Bluefruit LE modules

 Pick one up today in the adafruit shop!

 Adafruit invests time and resources providing this open source code,
 please support Adafruit and open-source hardware by purchasing
 products from Adafruit!

 MIT license, check LICENSE for more information
 All text above, and the splash screen below must be included in
 any redistribution
*********************************************************************/

#include <Arduino.h>
#include <SPI.h>
#if not defined (_VARIANT_ARDUINO_DUE_X_) && not defined (_VARIANT_ARDUINO_ZERO_)
  #include <SoftwareSerial.h>
#endif

#include "Adafruit_BLE.h"
#include "Adafruit_BluefruitLE_SPI.h"
#include "Adafruit_BluefruitLE_UART.h"

#include "BluefruitConfig.h"

/*=========================================================================
    APPLICATION SETTINGS

  FACTORYRESET_ENABLE     Perform a factory reset when running this sketch

                            Enabling this will put your Bluefruit LE module
                              in a 'known good' state and clear any config
                              data set in previous sketches or projects, so
                            running this at least once is a good idea.

                            When deploying your project, however, you will
                              want to disable factory reset by setting this
                              value to 0. If you are making changes to your
                            Bluefruit LE device via AT commands, and those
                              changes aren't persisting across resets, this
                              is the reason why. Factory reset will erase
                              the non-volatile memory where config data is
                              stored, setting it back to factory default
                              values.

                            Some sketches that require you to bond to a
                              central device (HID mouse, keyboard, etc.)
                              won't work at all with this feature enabled
                              since the factory reset will clear all of the
                              bonding data stored on the chip, meaning the
                              central device won't be able to reconnect.
    MINIMUM_FIRMWARE_VERSION  Minimum firmware version to have some new features
    MODE_LED_BEHAVIOUR        LED activity, valid options are
                              "DISABLE" or "MODE" or "BLEUART" or
                              "HWUART"  or "SPI"  or "MANUAL"
    -----------------------------------------------------------------------*/
    #define FACTORYRESET_ENABLE         1
    #define MINIMUM_FIRMWARE_VERSION    "0.6.6"
    #define MODE_LED_BEHAVIOUR          "MODE"
/*=========================================================================*/

// Pin Configuration and Firmware Declarations

#define LED_PIN       13

const unsigned long
  BLINKTIME =         100;

unsigned long 
  t_blink =           0L;

int
  blinkState =        LOW;

// Create the bluefruit object, either software serial...uncomment these lines
/*
SoftwareSerial bluefruitSS = SoftwareSerial(BLUEFRUIT_SWUART_TXD_PIN, BLUEFRUIT_SWUART_RXD_PIN);

Adafruit_BluefruitLE_UART ble(bluefruitSS, BLUEFRUIT_UART_MODE_PIN,
                      BLUEFRUIT_UART_CTS_PIN, BLUEFRUIT_UART_RTS_PIN);
*/

/* ...or hardware serial, which does not need the RTS/CTS pins. Uncomment this line */
// Adafruit_BluefruitLE_UART ble(Serial1, BLUEFRUIT_UART_MODE_PIN);

/* ...hardware SPI, using SCK/MOSI/MISO hardware SPI pins and then user selected CS/IRQ/RST */
Adafruit_BluefruitLE_SPI ble(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST);

/* ...software SPI, using SCK/MOSI/MISO user-defined SPI pins and then user selected CS/IRQ/RST */
//Adafruit_BluefruitLE_SPI ble(BLUEFRUIT_SPI_SCK, BLUEFRUIT_SPI_MISO,
//                             BLUEFRUIT_SPI_MOSI, BLUEFRUIT_SPI_CS,
//                             BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST);


// A small helper
void error(const __FlashStringHelper*err) {
  Serial.println(err);
  while (1);
}



/**************************************************************************/
/*!
    @brief  Sets up the HW an the BLE module (this function is called
            automatically on startup)
*/
/**************************************************************************/
void setup(void)
{
  pinMode(LED_PIN, OUTPUT);

  while (!Serial);  // required for Flora & Micro
  delay(500);

  Serial.begin(115600);
  Serial.println(F("Adafruit Bluefruit Command Mode Example"));
  Serial.println(F("---------------------------------------"));

  /* Initialise the module */
  Serial.print(F("Initialising the Bluefruit LE module: "));

  if ( !ble.begin() )
  {
    error(F("Couldn't find Bluefruit, make sure it's in CoMmanD mode & check wiring?"));
  }
  Serial.println( F("OK!") );

  if ( FACTORYRESET_ENABLE )
  {
    /* Perform a factory reset to make sure everything is in a known state */
    Serial.println(F("Performing a factory reset: "));
    if ( ! ble.factoryReset() ){
      error(F("Couldn't factory reset"));
    }
  }

  /* Disable command echo from Bluefruit */
  ble.echo(false);

  Serial.println("Requesting Bluefruit info:");
  /* Print Bluefruit information */
  ble.info();

  Serial.println(F("Please use Adafruit Bluefruit LE app to connect in UART mode"));
  Serial.println(F("Then Enter characters to send to Bluefruit"));
  Serial.println();

  ble.verbose(false);  // debug info is a little annoying after this point!

  /* Wait for connection */
  while (! ble.isConnected()) {
      delay(500);
  }

  // LED Activity command is only supported from 0.6.6
  if ( ble.isVersionAtLeast(MINIMUM_FIRMWARE_VERSION) )
  {
    // Change Mode LED Activity
    Serial.println(F("******************************"));
    Serial.println(F("Change LED activity to " MODE_LED_BEHAVIOUR));
    ble.sendCommandCheckOK("AT+HWModeLED=" MODE_LED_BEHAVIOUR);
    Serial.println(F("******************************"));
  }
}

/**************************************************************************/
/*!
    @brief  Constantly poll for new command or response data
*/
/**************************************************************************/
void loop(void)
{
  // Now Check for incoming characters from Bluefruit
  ble.println("AT+BLEUARTRX");
  ble.readline();
  ble.waitForOK();

  String BLEbuffer = ble.buffer;

  if (BLEbuffer.length() && BLEbuffer.indexOf("OK") == -1) 
    Serial.print(F("[Recv] ")); Serial.println(BLEbuffer);


  if (BLEbuffer.indexOf("Status") >= 0) {
    Serial.println(F("Status Request Received"));
    ble.print("AT+BLEUARTTX=");
    if (t_blink) {
      ble.println("BLNK");
    }
    else {
      if (blinkState)
        ble.println("ON");
      else
        ble.println("OFF");
    }

    // check response stastus
    if (! ble.waitForOK() ) {
      Serial.println(F("Failed to get response"));
    }

    ble.println("AT+BLEUARTRX");

  }

  else if (BLEbuffer.indexOf("0") >= 0) {
    blinkState = LOW;
    digitalWrite(LED_PIN, blinkState);
    analogWrite(13, 0);
    analogWrite(11, 0);
    digitalWrite(18, LOW);
    digitalWrite(19, LOW);
    digitalWrite(20, LOW);
    digitalWrite(21, LOW);
    t_blink = 0;
    ble.print("AT+BLEUARTTX=");
    ble.println("OFF");
    //Serial.println(F("OFF Request Received"));
    ble.println("AT+BLEUARTRX");
  }
  else if (BLEbuffer.indexOf("1") >= 0) {
    //if (!t_blink) t_blink = millis();
    analogWrite(13, 100);
    analogWrite(11, 100);
    digitalWrite(18, HIGH);
    digitalWrite(19, LOW);
    digitalWrite(20, LOW);
    digitalWrite(21, HIGH);
    ble.print("AT+BLEUARTTX=");
    ble.println("FORWARD");
    //Serial.println(F("BLINK Request Received"));
    ble.println("AT+BLEUARTRX");
  }
  else if (BLEbuffer.indexOf("2") >= 0) {
    blinkState = HIGH;
    digitalWrite(LED_PIN, blinkState);
    analogWrite(13, 100);
    analogWrite(11, 100);
    digitalWrite(18, LOW);
    digitalWrite(19, HIGH);
    digitalWrite(20, HIGH);
    digitalWrite(21, LOW);

    t_blink = 0;
    ble.print("AT+BLEUARTTX=");
    ble.println("BACK");
    //Serial.println(F("ON Request Received"));
    ble.println("AT+BLEUARTRX");
  }
  else if (BLEbuffer.indexOf("3") >= 0) {
    analogWrite(13, 100);
    analogWrite(11, 100);
    digitalWrite(18, HIGH);
    digitalWrite(19, LOW);
    digitalWrite(20, HIGH);
    digitalWrite(21, LOW);
    ble.print("AT+BLEUARTTX=");
    ble.println("LEFT");
    //Serial.println(F("BLINK Request Received"));
    ble.println("AT+BLEUARTRX");
  }
  else if (BLEbuffer.indexOf("4") >= 0) {
    //if (!t_blink) t_blink = millis();
    analogWrite(13, 100);
    analogWrite(11, 100);
    digitalWrite(18, LOW);
    digitalWrite(19, HIGH);
    digitalWrite(20, LOW);
    digitalWrite(21, HIGH);

    ble.print("AT+BLEUARTTX=");
    ble.println("RIGHT");
    //Serial.println(F("BLINK Request Received"));
    ble.println("AT+BLEUARTRX");
  }
  BLEbuffer = "";
}

All this should be doing every loop is reading in a line, picking out the character, and running the code block for that character. I don't see a reason for it to be lagging as the amount of data seems to be very minimal. Also there is little to no lag if I have it plugged in and the Serial Monitor running. As soon as I unplug it, it stays connected and everything still works except for the massive delay.

My initial thoughts were that the buffer was getting too full of "blank" commands, and that it had to process all of them before the real commands, but if that was the case then it would be lagging with the serial monitor open.

So far I have tried changing the BAUD rate to a lower number, thinking 300 was the minimum and if the problem was that the stack was getting too many commands to sort through 300 would be a tiny amount compared to the 115600 I had it at before, but this yielded no results. I have also tried cutting back the code and this seems to be the very minimum code I could use to make it still work.

I did read that it might help to apply the onSerialEvent() method, but when I try it gets stuck at:

if ( ble.isVersionAtLeast(MINIMUM_FIRMWARE_VERSION) )
      {
        // Change Mode LED Activity
        Serial.println(F("******************************"));
        Serial.println(F("Change LED activity to " MODE_LED_BEHAVIOUR));
        ble.sendCommandCheckOK("AT+HWModeLED=" MODE_LED_BEHAVIOUR);
        Serial.println(F("******************************"));
      }

Could it be that maybe its the wiring? For example, I might need a capacitor before the motors to keep them from having to "ramp up" to the power needed to actually start moving? I'm not super knowledgeable in electrical things but this was just a thought.


Solution

  • So the solution is very simple and I don't think it is a BLE only solution as it only has to do with the Serial commands (I.E. serial.print()). Having these commands in the code after the device is no longer connected to a computer will cause the board to not respond to them or take an extra long time trying to process them before eventually giving up (my guess as to what actually happens).

    The solution is to simply comment out all the Serial commands before you upload to the device with intent to run the code without being plugged in. Or you can obviously just erase them all but it might hinder your debugging experience the next time you are trying to edit the code.

    I figured instead of deleting the question, answering it with what I found out might help someone else in the future.