Extracting values from an incoming bluetooth serial on an arduino

Viewed 979

I am currently working on a project that involves an android app that controls an LED strip through an arduino. This is done via bluetooth module. The problem that I ran into is that the app involves features such as controlling the brightness and hue of the LED strip. So when a change is made to any of these components on the app, a value is sent to the arduino in order for it to update the values. Messages are sent such as this "bt+50!" where the first two letters identify which component is changed (bt = brightness), "+" indicating the beginning of the actual value and "!" indicating the end.

I would like help figuring out how I can split and splice the incoming messages so that I can first identify which component is changed and then be able to extract the integer values, something like:

if(message_id == 'bt'){
    brightness = message_value;
}

Currently I am just testing out things on a normal single color LED light. And I have only implemented very simple code that functions on single letter messages incoming from the bluetooth serial.

I have tried using the built in C string functions, but since I am new to C language and I am from a python background, it was difficult for me to get things working.

// Bluetooth module used - HC-06

#include <SoftwareSerial.h>
SoftwareSerial BlueTooth(5, 6); // (TXD, RXD) of HC-06

char BT_input; // to store input character received via BT.

void setup()  
{
  pinMode(13, OUTPUT);     // Arduino Board LED Pin
  BlueTooth.begin(9600);  
}

void loop() 
{
  if (BlueTooth.available())
  {
    BT_input=(BlueTooth.read());
    if (BT_input=='n')
    {
      digitalWrite(13, HIGH);
      BlueTooth.println("Now LED is ON");
    }
    else if (BT_input=='f')
    {
      digitalWrite(13, LOW);
      BlueTooth.println("Now LED is OFF");
    }
  }
}

I expect to have the message_id and message_value extracted from the incoming messages so that I can be able to update my LED strip.

2 Answers

You can have string variable which stores all the read values into it and once ! is read it will start processing it.

#include <SoftwareSerial.h>
SoftwareSerial BlueTooth(5, 6); // (TXD, RXD) of HC-06

char input; // to store input character received via BT.
String data;

void setup()  
{
  pinMode(13, OUTPUT);     // Arduino Board LED Pin
  BlueTooth.begin(9600);  
}

void loop() 
{
  if (BlueTooth.available())
  {
    input=(BlueTooth.read());

      if (input != '!') {
          data += input;
      }
      else{
          String message_id = String(data.substring(0,2)); //gets only "bt"
          data.remove(0,3); //data becomes "50" since '!' is not added to data

          int message_value = data.toInt();

          if(message_id == "bt"){
               brightness = message_value;
          }
      }
  }
}

You can use a typical trick used in cases as your, encoding multibyte character literals in a single unsigned int, that on Arduino uno and Mega 2560 is 16 bits in little endian format.

With reference to C standard ISO/IEC 9899:201x § "6.4.4.4 Character constants".

The subparagraph 10 explain our case:

An integer character constant has type int. The value of an integer character constant containing a single character that maps to a single-byte execution character is the numerical value of the representation of the mapped character interpreted as an integer. The value of an integer character constant containing more than one character (e.g., 'ab'), or containing a character or escape sequence that does not map to a single-byte execution character, is implementation-defined. If an integer character constant contains a single character or escape sequence, its value is the one that results when an object with type char whose value is that of the single character or escape sequence is converted to type int.

Where 'implementation-defined' in our case is managed as described below.

In this case the multibyte char constant 'bt' can be coded as the 16 bits integer 0x6274, where 'b'=0x62 and 't'=0x74.

The compiler should also be smart enough to convert the multibyte char sequence in the int value.

In the following snippet we consider that the char array msg holds the received message, and we use a simple and functional switch statement (that requires an integer value) casting the msg variable to an unsigned integer.:

char msg[10];
...
switch (*((unsigned int *)msg))
{
    case 'tb':     //Note the reverse order of command characters due to endianess
        int value = atoi(msg+2);    //Convert number to int
        ....     //do something
    break;

    ....    //other cases
}

casting the msg pointer variable to a pointer to unsigned integer the compiler will interpret the first 2 chars as the integer in the way explained above, and act the switch based on their value.

The following sample use your code modified for use of a switch. It assumes that the command has a fixed length equal to MAX_MSG_LEN (2 characters for command, 2 characters for the value and the end of message):

// Bluetooth module used - HC-06

#include <SoftwareSerial.h>
SoftwareSerial BlueTooth(5, 6); // (TXD, RXD) of HC-06

#define MAX_MSG_LEN 5       //Max message length
#define OFFSET_TO_VALUE     //Offset in input buffer to value

char BT_input[10];  // to store input characters received via BT.

void setup()
{
    pinMode(13, OUTPUT);    // Arduino Board LED Pin
    BlueTooth.begin(9600);
}

void loop()
{
    if (BlueTooth.available())
    {
        /*
         * Read in the message up to the '!'
         */
        int i=0;
        do
        {
            BT_input[i] = (BlueTooth.read());
        } while (i<MAX_MSG_LEN && BT_input[i++]!='!');

        /*
         * If message length is exactly what we expect
         * we can process the message.
         * Note that because of endianess the command
         * chare are rversed.
         */
        if (i == MAX_MSG_LEN)
        {
            switch (*((unsigned int *)BT_input))
            {
                case 'tb':  // command 'bt'
                    process_brigthness(atoi(BT_input + OFFSET_TO_VALUE));
                    break;

                case 'no':  // command 'on'
                {
                    digitalWrite(13, HIGH);
                    BlueTooth.println("Now LED is ON");
                    break;
                }

                case 'fo':  // command 'of' for off
                {
                    digitalWrite(13, LOW);
                    BlueTooth.println("Now LED is OFF");
                    break;
                }

                default:    // unknown command
                {
                    unknown_command();
                    break;
                }
            }
        }
        else
        {
            /*
             * Process communication error
             */
            communication_error();
        }
    }
}

Or using an union of the input stream and the command structure:

// Bluetooth module used - HC-06

#include <SoftwareSerial.h>
SoftwareSerial BlueTooth(5, 6); // (TXD, RXD) of HC-06

#define MAX_MSG_LEN 5       //Max message length
#define OFFSET_TO_VALUE     //Offset in input buffer to value

union tag_BT_input              // to store input characters received via BT.
{
    char   stream[MAX_MSG_LEN];
    struct
    {
        unsigned int cmd;       //Command
        char         val[2];    //value
        char         eom;       //End of message marker '!'
    }msg;
} BT_input;

void setup()
{
    pinMode(13, OUTPUT);    // Arduino Board LED Pin
    BlueTooth.begin(9600);
}

void loop()
{
    if (BlueTooth.available())
    {
        /*
         * Read in the message up to the '!'
         */
        int i=0;
        do
        {
            BT_input.stream[i] = (BlueTooth.read());
        } while (i<MAX_MSG_LEN && BT_input.stream[i++]!='!');

        /*
         * If message length is exactly what we expect
         * we can process the message.
         * Note that because of endianess the command
         * chare are rversed.
         */
        if (i == MAX_MSG_LEN)
        {
            switch (BT_input.msg.cmd)
            {
                case 'tb':  // command 'bt'
                    process_brigthness(atoi(BT_input.msg.val));
                    break;

                case 'no':  // command 'on'
                {
                    digitalWrite(13, HIGH);
                    BlueTooth.println("Now LED is ON");
                    break;
                }

                case 'fo':  // command 'of' for off
                {
                    digitalWrite(13, LOW);
                    BlueTooth.println("Now LED is OFF");
                    break;
                }

                default:    // unknown command
                {
                    unknown_command();
                    break;
                }
            }
        }
        else
        {
            /*
             * Process communication error
             */
            communication_error();
        }
    }
}
Related