Go Down

Topic: Serial Read, values sent to Serial via Bluetooth (Read 5761 times) previous topic - next topic

geek_tk

I'm working on building an Android app that will control a camera via Arduino. My app sends a long string of text to my Arduino via Bluetooth:

ExposureTime=5000, FocusTap=500, Interval=4000

I know how to get the Arduino IDE to display the above text in the Serial Window when it is sent over Bluetooth, but I'm, not sure how to take the next step and get the Arduino to read the values and then use them as variables in my loop. Is there an easy way to set up the Serial.Read() to handle it?


PaulS

Quote
I know how to get the Arduino IDE to display the above text in the Serial Window when it is sent over Bluetooth

Great. That's 2/3 of the battle. Let's see that code.

Quote
but I'm, not sure how to take the next step and get the Arduino to read the values

You said that you were already reading the data. Did you mean that you want the Arduino to understand/save/parse the data, too?

Quote
and then use them as variables in my loop.

Well, that's going to be hard, since you are sending constants. You can have the Arduino parse the data, and convert the strings to int values, and store those values in variables, for later/other use.

geek_tk

Thanks for the reply. I used this code ("bluetooth_LED_example") as a test to get the Arduino to turn on/off the LED when a 0 or 1 was sent via my Android phone.

Quote

/*
simple LED test
*/

char val;         // variable to receive data from the serial port
int ledpin = 2;  // LED connected to pin 2 (on-board LED)

void setup()
{
  pinMode(ledpin = 13, OUTPUT);  // pin 13 (on-board LED) as OUTPUT
 
  Serial.begin(115200);       // start serial communication at 115200bps
 
}
 
void loop() {
  if( Serial.available() )       // if data is available to read
  {;}
    val = Serial.read();         // read it and store it in 'val'
 
  if( val == '0' )               // if '0' was received led 13 is switched off

  {
   digitalWrite(ledpin, LOW);    // turn Off pin 13 off
delay(1000);                  // waits for a second   
Serial.println("13 off");
  }

if( val == '1' )               // if '1' was received led 13 on
 {
    digitalWrite(ledpin = 13, HIGH);  // turn ON pin 13 on
    delay(1000);                  // waits for a second
    Serial.println("13 on");
  }
}



Then I modified the code so that my phone would send over the camera parameters and those would show up in the serial window. So I guess my question is - how do I get the Arduino to read and store those values (the #s - ie the ExposureLength=20000) in the loop? Do I need to declare "ExposureLength" as a char and then somehow get the serial to read it's value (20000) and store it somewhere so the loop can read it?

I am a complete newb with Arduino, and I'm certain this is a fairly common task that people employ...I've read through "Getting Started With Arduino" and "Beginning Arduino" and can't find anything specific. Apologies if I am being unclear. Thanks!

PaulS

Code: [Select]
  if( Serial.available() )       // if data is available to read
  {;}

If there is data available to read, do nothing.

Then, regardless of whether or not there is serial data available:
Code: [Select]
    val = Serial.read();         // read it and store it in 'val'
read it.

Quote
Then I modified the code so that my phone would send over the camera parameters and those would show up in the serial window.

Your phone should be sending something like
<ExposureLength=20000, NextParameter=value, AnotherParameter=anotherValue>

Then, you can use code like this to get the serial data into an array:
Code: [Select]

#define SOP '<'
#define EOP '>'

bool started = false;
bool ended = false;

char inData[80];
byte index;

void setup()
{
   Serial.begin(57600);
   // Other stuff...
}

void loop()
{
  // Read all serial data available, as fast as possible
  while(Serial.available() > 0)
  {
    char inChar = Serial.read();
    if(inChar == SOP)
    {
       index = 0;
       inData[index] = '\0';
       started = true;
       ended = false;
    }
    else if(inChar == EOP)
    {
       ended = true;
       break;
    }
    else
    {
      if(index < 79)
      {
        inData[index] = inChar;
        index++;
        inData[index] = '\0';
      }
    }
  }

  // We are here either because all pending serial
  // data has been read OR because an end of
  // packet marker arrived. Which is it?
  if(started && ended)
  {
    // The end of packet marker arrived. Process the packet

    // Reset for the next packet
    started = false;
    ended = false;
    index = 0;
    inData[index] = '\0';
  }
}


Where it says "Process the packet", you can then use strtok() to get the bits from the array. Use "=" as the delimiter to get the ExposureLength name. Then, use "," as the delimiter to get the value as a string ("20000") and atoi() to convert the string to a number, 20000. Then, store the value in a variable - which one depends on the name token (use strcmp() to detect a match).

Quote
I'm certain this is a fairly common task that people employ

It is.

Quote
I've read through "Getting Started With Arduino" and "Beginning Arduino" and can't find anything specific.

It is not a beginner project, though. You can use the code I posted above as a starting point, and google strtok (NOT strtok_r), strcmp, and atoi, to learn how to use them.

geek_tk

Thanks Paul! More than enough for me to get lost in for a few days and try to learn some new tricks. I really appreciate your help. If/when i get this all working I will be sure to post the finished code here for the beenfit of anyone else looking to do the same thing.

geek_tk

Paul I have just started to dive into your strtok suggestions and to learn about arrays. Before I got there though I have adopted new code for the Android / Bluetooth / Arduino connection with the SoftwareSerial library that simply reads what my phone is sending via the BlueSmirf and prints it to the Arduino serial window.

All was well until I tried to add an LCD screen and have that also display what is being written to the Arduino. Now, if I send "testing" from the phone, the Arduino serial window prints "tsg" or sometimes "stg" (it is only getting some of the letters).

I know that the issue stems from the multiple instances of the serial communication occurring at the same time, how do I add an interrupt or some other code to create a buffer?

It is worth noting also that peculiarly, the below code does not work unless the BlueSmirf RX/TX are wired to the Arduino 1,0. Despite defining the RX/TX pins as Arduino 7,6 connecting the wires from the BlueSmirf to these pins does not send text from the phone to the Arduino.  I tried defining the Arduino pins 1,0 as RX,TX but then the Serial monitor showed only gibberish characters.

here is the code:

Code: [Select]
/*******************************************\
BlueSMiRF I/O Example

Sends and recieves single-bytes between the
client-USB connection and the tethered
Bluetooth device.

Jeremy Bridon
jgbridon@gmail.com
\********************************************/

// Included for serial communication
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Define pins you're using for serial communication
// for the BlueSMiRF connection
#define TXPIN 6
#define RXPIN 7

// Create an instance of the software serial object
SoftwareSerial BlueSerial(RXPIN, TXPIN);

// Main application entry point
void setup()
{
   // Define the appropriate input/output pins
   pinMode(RXPIN, INPUT);
   pinMode(TXPIN, OUTPUT);

   // Begin communicating with the bluetooth interface
   Serial.begin(115200);
   BlueSerial.begin(115200);

   // Say we are starting the serial com
   Serial.println("Serial start!");
   BlueSerial.println("Serial start!");
   //turn on the lcd, 20 columns and four rows
   lcd.begin(16, 2);
   // print the name of the code to tjhe LCD
   lcd.print("Program Start!");
   //wait six seconds
   delay(6000);
   //clear the LCD
   lcd.clear();
   
}

// Main application loop
void loop()
{
   // Wait for command-line input
   if(Serial.available() > 0)
   {
       // Read off all bytes
       BlueSerial.print( Serial.write(Serial.read()));
       delay(100);
       lcd.write(Serial.read());
   }
}

PaulS

Code: [Select]
        BlueSerial.print( Serial.write(Serial.read()));
        delay(100);
        lcd.write(Serial.read());

You have two read()s. Each one is going to get part of the data.

Code: [Select]
int val = Serial.read();
BlueSerial.print(val);
lcd.write(val);


The Serial.write() function returns a value - the number of characters written. It is that value that you are trying to send to the BlueSerial port. Why?

geek_tk

I see, so the two read ()s split the data being sent in. How would I put in a buffer or some sort of a delay so that what is read by the first read() is also sent to the lcd?

PaulS

Quote
How would I put in a buffer or some sort of a delay so that what is read by the first read() is also sent to the lcd?

I showed you how.

geek_tk

Oh, duh! I'm reading this on my phone and I missed the code you posted. Thanks!

geek_tk

OK Paul, giving strtok() a go. Apologies in advance for the shallowness of knowledge I am sure to display.

I've modified my Android App so the parameters are sent over in the following format:

FT=500, EXP=20000, INT=5000, EXPDLY=500, MTR=100

I removed the LCD from the code to streamline it a bit and will add that back in later, but I'm still stuck on the strtok() useage. I found some examples in older forum posts where you helped others, but haven't come across a good tutorial that explains the syntax. I tried to insert come code that would be a starting point:

Code: [Select]
// Included for serial communication
#include <SoftwareSerial.h>
// Define pins you're using for serial communication
// for the BlueSMiRF connection
#define TXPIN 6
#define RXPIN 7
// Create an instance of the software serial object
SoftwareSerial BlueSerial(RXPIN, TXPIN);
// array definitions
// define SOP (start of packet) and EOP (end of packet)
#define SOP '<'
#define EOP '>'


bool started = false;
bool ended = false;

char inData[80]; // creates an 80 character array called "inData"
byte index; //creates a variable type=byte called "index"
unsigned int FT; //variable for Focus Tap
unsigned int EXP; //variable for Exposure
unsigned int INT; // variable for interval
unsigned int EXPDLY; //variable for Exposure Delay
unsigned int MTR; // variable for Motor Speed

// Main application entry point
void setup()
{
    // Define the appropriate input/output pins
    pinMode(RXPIN, INPUT);
    pinMode(TXPIN, OUTPUT);

    // Begin communicating with the bluetooth interface
    Serial.begin(115200);
    BlueSerial.begin(115200);

    // Say we are starting the serial com
    BlueSerial.println("Serial start!");
       
}

// Main application loop
void loop()
{
    // Wait for command-line input
    if(Serial.available() > 0)
    {
        // Read off all bytes
        int val = Serial.read();
       // BlueSerial.print(val);
        Serial.write(val);
        delay(100); 
    }
  {
 

// Read all serial data available, as fast as possible
  while(Serial.available() > 0)
  {
    char inChar = Serial.read(); //PaulS - do I need to change "Serial.read()" to "val?" because of the double read() issue you helped me solve before?
    if(inChar == SOP)
    {
       index = 0;
       inData[index] = '\0';
       started = true;
       ended = false;
    }
    else if(inChar == EOP)
    {
       ended = true;
       break;
    }
    else
    {
      if(index < 79)
      {
        inData[index] = inChar;
        index++;
        inData[index] = '\0';
      }
    }
  }

  // We are here either because all pending serial
  // data has been read OR because an end of
  // packet marker arrived. Which is it?
  if(started && ended)
  {
  /* The end of packet marker arrived. Process the packet
  Where it says "Process the packet", you can then use strtok() to get
  the bits from the array. Use "=" as the delimiter to get the
  ExposureLength name. Then, use "," as the delimiter to get the
  value as a string ("20000") and atoi() to convert the string to
  a number, 20000. Then, store the value in a variable - which one
  depends on the name token (use strcmp() to detect a match).*/
char *token = strtok(inData, ","); // do I need to change "token" to one of my variables (i.e FT, EXP, INT etc. and repeat this codeblock for each one)?
while(token)
{
   int iVal = atoi(token); // not sure what "iVal" needs to be changed to here, or how I would use strcmp()

   token = strtok(NULL, "="); // Is this where I would use the "=" in the parsing with strcmp()?
}


    // Reset for the next packet
    started = false;
    ended = false;
    index = 0;
    inData[index] = '\0';
  }
}
   
   
}


Thanks for your help Paul!

PaulS

If you have input like "FT=500, EXP=20000, INT=5000, EXPDLY=500, MTR=100", and you call strtok() with that string, and a delimiter of ",", the first token that you get will be "FT=500".

What I think you want to do is more like this:
Code: [Select]
char *name = strtok(inData, "=");
while(name)
{
  char *valToken = strtok(NULL, ",");
  if(valToken)
  {
     int val = atoi(valToken);
     if(strcmp(name, "FT") == 0)
        ftVal = val;
     else if(strcmp(name, "EXP")
        expVal = val;
     // More else if's go here
  }

  name = strtok(NULL, "=");
}

Go Up