I have the following code on my Arduino that constantly checks for a serial command that's sent over TCP using a Wifly library.
What the following code does is split a string like the following when sent over serial:
{power,tv}
It sets these properties accordingly:
char command[32];
char value[32];
It then executes certain methods using sendCommand(command, value); based on the properties set in the loop below.
Keep in mind this works just fine using the Wifly library.
void loop() {
Client client = server.available();
if (client) {
boolean start_data = false;
boolean next = false;
char command[32];
char value[32];
int index = 0;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.print(c);
if (c == '}') {
break;
}
if(start_data == true) {
if(c != ',') {
if(next)
value[index] = c;
else
command[index] = c;
index++;
} else {
next = true;
command[index] = '\0';
index = 0;
}
}
if (c == '{') {
start_data = true;
}
}
}
value[index] = '\0';
client.flush();
client.stop();
sendCommand(command,value);
}
}
Instead of using WiFi I've purchased some Xbee modules. They basically allow you to send serial bytes as well. The only problem is that I'm not quite sure how to handle the looping considering there's no while(client.connected()) anymore. Instead of that I've used while(Serial.available()) thinking that will work, but it doesn't set the value property for some reason.
I get command but I don't get value.
Also I'm not sure whether the loop above is the best way of doing what I'm after, all I know is that it works just fine the way it is. :)
Here is my new loop, which only returns command and not value for some reason:
void loop() {
// if there are bytes waiting on the serial port
if (Serial.available()) {
boolean start_data = false;
boolean next = false;
char command[32];
char value[32];
int index = 0;
while (Serial.available()) {
char c = Serial.read();
Serial.print(c);
if (c == '}') {
break;
}
if(start_data == true) {
if(c != ',') {
if(next)
value[index] = c;
else
command[index] = c;
index++;
} else {
next = true;
command[index] = '\0';
index = 0;
}
}
if (c == '{') {
start_data = true;
}
}
value[index] = '\0';
sendCommand(command,value);
}
}
If the following works with the new loop, I'll be very happy!
void sendCommand(char *command, char *value) {
// do something wonderful with command and value!
}
Got it working by using the following code:
#define SOP '{'
#define EOP '}'
bool started = false;
bool ended = false;
char inData[80];
byte index;
void setup()
{
Serial.begin(9600);
// 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
char *cmd = strtok(inData, ",");
if(cmd)
{
char *val = strtok(NULL, ",");
if(val)
{
sendCommand(cmd, val);
}
}
// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
}
I would change the structure similar to:
while( c != '}') {
if (Serial.available()) {
.
.
.
}
}
Serial characters are received significantly slower then the loop.
Related
I need to exit a 'for' loop running in the main loop on an Arduino board through a serial command, but it looks like that up to the end of the main loop the Serial Event is not taken in consideration. How can I solve this?
I supply some simplified code to have a better understanding of the problem:
boolean start = false;
boolean abortLoop = false;
String inputString = "";
void setup() {
Serial.begin(9600);
}
void loop() {
if(start) {
Serial.println("start");
for(int i=0; i <10; i++) {
if(abortLoop)
break;
Serial.println(i);
delay(1000);
}
start = false;
abortLoop = false;
}
}
void serialEvent()
{
while (Serial.available())
{
// Get the new byte:
char inChar = (char)Serial.read();
// Add it to the inputString:
inputString += inChar;
//Serial.println(inputString);
// If the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inChar == '\n')
{
inputString = inputString.substring(0, inputString.length() - 1);
//Serial.println(inputString);
//Serial.println(inputString.length());
if (inputString[0] == 's')
{
Serial.println("start");
start = true;
}
if (inputString[0] == 'a')
{
abortLoop = true;
Serial.println("abort");
}
inputString = "";
}
}
}
If sending the command 'a' in the middle of the loop, the loop keeps going up to the end.
The result is:
start
start
0
1
2
3
4
5
6
7
8
9
abort
abort
In this case, the 'a' command was sent two times.
The solution is pretty simple. The only thing you have to do is to call your function in the for loop, else the function can't be executed.
// ...
for(int i=0; i <10; i++) {
serialEvent();
if(abortLoop)
break;
Serial.println(i);
delay(1000);
}
// ...
Moreover, I think it's better if you call your function at this place too:
// ...
void loop() {
serialEvent();
if(start) {
// ...
It's strangely not necessary, but in my opinion it's better. And 'start' is printed out two times, because you call it two times. One time here:
void loop() {
if(start) {
Serial.println("start");
for(int i=0; i <10; i++) {
and another time here:
if (inputString[0] == 'a')
{
abortLoop = true;
Serial.println("abort");
}
so you can remove one of them.
I have to read the incoming SMS on my gsm module SIM900 (which is connected to Arduino), and I want to print the sender number and message on to serial monitor.
I first configure gsm module with AT commands and Response() function will give me the response to AT commands.
as any SMS will be in the following pattern
+CMT: "[Mobile number]", "[Date and Time]"
[message body]
So, I first extract +CMT and after that I will take mobile number and atlast we have message body. The code I have used is
char RcvdMsg[200] = "";
int RcvdCheck = 0;
int RcvdConf = 0;
int index = 0;
int RcvdEnd = 0;
char MsgMob[15];
char MsgTxt[50];
int MsgLength = 0;
void Config() // This function is configuring our SIM900 module i.e. sending the initial AT commands
{
delay(1000);
Serial.print("ATE0\r");
Response();
Serial.print("AT\r");
Response();
Serial.print("AT+CMGF=1\r");
Response();
Serial.print("AT+CNMI=1,2,0,0,0\r");
Response();
}
void setup()
{
Serial.begin(9600);
Config();
}
void loop()
{
RecSMS();
}
void Response() // Get the Response of each AT Command
{
int count = 0;
Serial.println();
while(1)
{
if(Serial.available())
{
char data =Serial.read();
if(data == 'K'){Serial.println("OK");break;}
if(data == 'R'){Serial.println("GSM Not Working");break;}
}
count++;
delay(10);
if(count == 1000){Serial.println("GSM not Found");break;}
}
}
void RecSMS() // Receiving the SMS and extracting the Sender Mobile number & Message Text
{
if(Serial.available())
{
char data = Serial.read();
if(data == '+'){RcvdCheck = 1;}
if((data == 'C') && (RcvdCheck == 1)){RcvdCheck = 2;}
if((data == 'M') && (RcvdCheck == 2)){RcvdCheck = 3;}
if((data == 'T') && (RcvdCheck == 3)){RcvdCheck = 4;}
if(RcvdCheck == 4){RcvdConf = 1; RcvdCheck = 0;}
if(RcvdConf == 1)
{
if(data == '\n'){RcvdEnd++;}
if(RcvdEnd == 3){RcvdEnd = 0;}
RcvdMsg[index] = data;
index++;
if(RcvdEnd == 2){RcvdConf = 0;MsgLength = index-2;index = 0;}
if(RcvdConf == 0)
{
Serial.print("Mobile Number is: ");
for(int x = 4;x < 17;x++)
{
MsgMob[x-4] = RcvdMsg[x];
Serial.print(MsgMob[x-4]);
}
Serial.println();
Serial.print("Message Text: ");
for(int x = 46; x < MsgLength; x++)
{
MsgTxt[x-46] = RcvdMsg[x];
Serial.print(MsgTxt[x-46]);
}
Serial.println();
Serial.flush();
}
}
}
}
The problem of the code is
After receiving first SMS I am getting my mobile number and message body. After that I am only getting sender number printed on to my serial monitor but not the message body.
Where It has gone wrong. I could not understood.
Please help me.......Thanks in advance.
If it does work the first time, but not subsequent times it probably has to do with some variables not being reset. You declare all of your variables at the top of the file even though they are only needed in the RecSMS() function. Try moving the declarations to the top of RecSMS().
void RecSMS() {
char RcvdMsg[200] = "";
int RcvdCheck = 0;
int RcvdConf = 0;
int index = 0;
int RcvdEnd = 0;
char MsgMob[15];
char MsgTxt[50];
int MsgLength = 0;
if(Serial.available()) {
// Rest of the code goes here
Thankyou #Michael. I think this also solves the issue.
The problem I found in the code is, we are not resetting all the variables in RecSMS function. So to solve this keep this below code before the Serial.flush() statement.
RcvdCheck = 0;
RcvdConf = 0;
index = 0;
RcvdEnd = 0;
MsgMob[15];
MsgTxt[50];
MsgLength = 0;
This will solve the problem
I had purchased a microSD card shield from sparkfun and I wish to do data logging to read 2 MPU6050. I wished to have sampling frequency of 100Hz, so I came across one example from Greiman about lowlatencylogger. Using his example and SDFat library instead of SD library, I do can get very good sampling frequency, however, I cannot terminate the data logging at will.
I tried to add a switch to set a condition where if the arduino read the switch, then it will start truncating the file. But so far no result. So can anyone help me??
the code that I am using
#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h>
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
//------------------------------------------------------------------------------
// User data functions. Modify these functions for your data items.
#include "UserDataType.h" // Edit this include file to change data_t.
MPU6050 mpu;
MPU6050 mpu_69 (0x69);
int switchPin = 6;
void setupData() {
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
// set I2C 400 kHz
TWBR = (F_CPU/400000 - 16)/2;
Serial.println(F("Using Wire"));
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
Serial.println(F("Using Fastwire"));
#endif
mpu.initialize();
mpu_69.initialize();
pinMode(switchPin,INPUT_PULLUP);
}
// Acquire a data record.
void acquireData(data_t* data) {
data->time = micros();
data->hs = analogRead(0);
data->to = analogRead(1);
mpu.getMotion6(&data->axr, &data->ayr, &data->azr,
&data->gxr, &data->gyr, &data->gzr);
mpu_69.getMotion6(&data->axl, &data->ayl, &data->azl,
&data->gxl, &data->gyl, &data->gzl);
}
// Print a data record.
void printData(Print* pr, data_t* data) {
pr->print(data->time);
pr->write(',');
pr->print(data->hs);
pr->write(',');
pr->print(data->to);
pr->write(',');
pr->print(data->axr);
pr->write(',');
pr->print(data->ayr);
pr->write(',');
pr->print(data->azr);
pr->write(',');
pr->print(data->gxr);
pr->write(',');
pr->print(data->gyr);
pr->write(',');
pr->print(data->gzr);
pr->write(',');
pr->print(data->axl);
pr->write(',');
pr->print(data->ayl);
pr->write(',');
pr->print(data->azl);
pr->write(',');
pr->print(data->gxl);
pr->write(',');
pr->print(data->gyl);
pr->write(',');
pr->println(data->gzl);
}
// Print data header.
void printHeader(Print* pr) {
pr->println(F("micros,heels,toeo,axshank,ayshank,azshank,gxshank,gyshank,gzshank,axankle,ayankle,azankle,gxankle,gyankle,gzankle"));
}
//==============================================================================
// Start of configuration constants.
//==============================================================================
//Interval between data records in microseconds.
const uint32_t LOG_INTERVAL_USEC = 10000;
//------------------------------------------------------------------------------
// Pin definitions.
//
// SD chip select pin.
const uint8_t SD_CS_PIN = 8;
//
// Digital pin to indicate an error, set to -1 if not used.
// The led blinks for fatal errors. The led goes on solid for SD write
// overrun errors and logging continues.
const int8_t ERROR_LED_PIN = -1;
//------------------------------------------------------------------------------
// File definitions.
//
// Maxrimum file size in blocks.
// The program creates a contiguous file with FILE_BLOCK_COUNT 512 byte blocks.
// This file is flash erased using special SD commands. The file will be
// truncated if logging is stopped early.
const uint32_t FILE_BLOCK_COUNT = 256000;
////////////////////////////
uint32_t const ERASE_SIZE = 262144L;
void logData() {
unsigned long timeout =0;
bool truncate=false;
uint32_t bgnBlock, endBlock;
// Allocate extra buffer space.
block_t block[BUFFER_BLOCK_COUNT];
block_t* curBlock = 0;
Serial.println();
// Find unused file name.
if (BASE_NAME_SIZE > 6) {
error("FILE_BASE_NAME too long");
}
while (sd.exists(binName)) {
if (binName[BASE_NAME_SIZE + 1] != '9') {
binName[BASE_NAME_SIZE + 1]++;
} else {
binName[BASE_NAME_SIZE + 1] = '0';
if (binName[BASE_NAME_SIZE] == '9') {
error("Can't create file name");
}
binName[BASE_NAME_SIZE]++;
}
}
// Delete old tmp file.
if (sd.exists(TMP_FILE_NAME)) {
Serial.println(F("Deleting tmp file"));
if (!sd.remove(TMP_FILE_NAME)) {
error("Can't remove tmp file");
}
}
// Create new file.
Serial.println(F("Creating new file"));
binFile.close();
if (!binFile.createContiguous(sd.vwd(),
TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
error("createContiguous failed");
}
// Get the address of the file on the SD.
if (!binFile.contiguousRange(&bgnBlock, &endBlock)) {
error("contiguousRange failed");
}
// Use SdFat's internal buffer.
uint8_t* cache = (uint8_t*)sd.vol()->cacheClear();
if (cache == 0) {
error("cacheClear failed");
}
// Flash erase all data in the file.
Serial.println(F("Erasing all data"));
uint32_t bgnErase = bgnBlock;
uint32_t endErase;
while (bgnErase < endBlock) {
endErase = bgnErase + ERASE_SIZE;
if (endErase > endBlock) {
endErase = endBlock;
}
if (!sd.card()->erase(bgnErase, endErase)) {
error("erase failed");
}
bgnErase = endErase + 1;
}
// Start a multiple block write.
if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) {
error("writeBegin failed");
}
// Initialize queues.
emptyHead = emptyTail = 0;
fullHead = fullTail = 0;
// Use SdFat buffer for one block.
emptyQueue[emptyHead] = (block_t*)cache;
emptyHead = queueNext(emptyHead);
// Put rest of buffers in the empty queue.
for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) {
emptyQueue[emptyHead] = &block[i];
emptyHead = queueNext(emptyHead);
}
// Wait for Serial Idle.
Serial.flush();
delay(10);
uint32_t bn = 0;
uint32_t t0 = millis();
uint32_t t1 = t0;
uint32_t overrun = 0;
uint32_t overrunTotal = 0;
uint32_t count = 0;
uint32_t maxlatency = 0;
int32_t diff;
// Start at a multiple of interval.
uint32_t logTime = micros()/LOG_INTERVAL_USEC + 1;
logTime *= LOG_INTERVAL_USEC;
bool closeFile = false;
while (1) {
// Time for next data record.
logTime += LOG_INTERVAL_USEC;
if (Serial.available()) {
closeFile = true;
}
if (closeFile) {
if (curBlock != 0 && curBlock->count >= 0) {
// Put buffer in full queue.
fullQueue[fullHead] = curBlock;
fullHead = queueNext(fullHead);
curBlock = 0;
}
} else {
if (curBlock == 0 && emptyTail != emptyHead) {
curBlock = emptyQueue[emptyTail];
emptyTail = queueNext(emptyTail);
curBlock->count = 0;
curBlock->overrun = overrun;
overrun = 0;
}
do {
diff = logTime - micros();
} while(diff > 0);
if (diff < -10) {
error("LOG_INTERVAL_USEC too small");
}
if (curBlock == 0) {
overrun++;
} else {
acquireData(&curBlock->data[curBlock->count++]);
if (curBlock->count == DATA_DIM) {
fullQueue[fullHead] = curBlock;
fullHead = queueNext(fullHead);
curBlock = 0;
}
}
}
if (fullHead == fullTail) {
// Exit loop if done.
if (closeFile) {
break;
}
} else if (!sd.card()->isBusy()) {
// Get address of block to write.
block_t* pBlock = fullQueue[fullTail];
fullTail = queueNext(fullTail);
// Write block to SD.
uint32_t usec = micros();
if (!sd.card()->writeData((uint8_t*)pBlock)) {
error("write data failed");
}
usec = micros() - usec;
t1 = millis();
if (usec > maxlatency) {
maxlatency = usec;
}
count += pBlock->count;
// Add overruns and possibly light LED.
if (pBlock->overrun) {
overrunTotal += pBlock->overrun;
if (ERROR_LED_PIN >= 0) {
digitalWrite(ERROR_LED_PIN, HIGH);
}
}
// Move block to empty queue.
emptyQueue[emptyHead] = pBlock;
emptyHead = queueNext(emptyHead);
bn++;
if (bn == FILE_BLOCK_COUNT) {
// File full so stop
break;
}
}
}
if (!sd.card()->writeStop()) {
error("writeStop failed");
}
do{
if(digitalRead(switchPin)==LOW){
if(millis()>timeout){ // switchPin is low and has been low for 100ms
truncate = true; // close the file,
// truncate at current record number,
// rename to 'binname'
}
}
else { // switchPin High, not pressed
timeout = millis() + 100; // time that switch has to be pressed to be valid
}
}while(truncate=false);
if(truncate == true){
// Truncate file if recording stopped early.
if (bn != FILE_BLOCK_COUNT) {
//Serial.println(F("Truncating file"));
if (!binFile.truncate(512L * bn)) {
error("Can't truncate file");
}
}
}
binFile.close();
if (!binFile.rename(sd.vwd(), binName)) {
error("Can't rename file");
}
}
//------------------------------------------------------------------------------
void setup(void) {
if (ERROR_LED_PIN >= 0) {
pinMode(ERROR_LED_PIN, OUTPUT);
}
Serial.begin(38400);
sd.begin(38400);
while (!Serial) {}
pinMode(switchPin,INPUT_PULLUP);
Serial.print(F("FreeRam: "));
Serial.println(FreeRam());
Serial.print(F("Records/block: "));
Serial.println(DATA_DIM);
if (sizeof(block_t) != 512) {
error("Invalid block size");
}
setupData();
// initialize file system.
if (!sd.begin(SD_CS_PIN, SPI_FULL_SPEED)) {
sd.initErrorPrint();
fatalBlink();
}
}
//------------------------------------------------------------------------------
void loop(void) {
logData();
binaryToCsv();
}
You need to modify the following code segment:
if (Serial.available()) {
closeFile = true;
}
To be something like:
if (Serial.available() || (stopSwitch(haltPIN) == LOW)) {
closeFile = true;
}
Where stopSwitch is normally HIGH connected to haltPIN
The program listen to messages from serial port in the form or where first character (A or D) means analog or digital, the 2nd character - pin, the 3rd character - 1/0 or from 0 to 255. The markers < and > show the beginning and the end of packet.
For example, if packet is received, the light is turned on by digitalWrite(13,1)
But nothing happens. When I send via serial monitor, for instance: light is supposed to blink but it does not. The same with analogue outputs.
bool started = false;
bool ended = false;
char inData[5];
byte index;
void setup()
{
Serial.begin(9600);
}
void loop()
{
while (Serial.available() > 0)
{
char inChar = Serial.read();
if (inChar == '<')
{
index = 0;
started = true;
ended = false;
}
else if (inChar == '>')
{
ended = true;
break;
}
else
{
if (index <= 4)
{
inData[index] = inChar;
index++;
}
}
if (started && ended)
{
if (inData[0] == 'A')
{
pinMode(inData[2],OUTPUT);
analogWrite(inData[2],inData[4]);
}
else if (inData[0] == 'D')
{
if (inData[4] == 1)
{
pinMode(inData[2],OUTPUT);
digitalWrite(inData[2],HIGH);
}
else if (inData[4] == 0)
{
pinMode(inData[2],OUTPUT);
digitalWrite(inData[2],LOW);
}
}
started = false;
ended = false;
index = 0;
}
}
Serial.println("Sending");
}
The following code will allow you to execute a method with an example serial string:
<power,led>
Once it processes this string, it'll execute the following method:
sendCommand(cmd, val);
See below for an example of how to turn on an LED on PIN 11.
#include <avr/pgmspace.h>
int IRledPin = 11;
#define SOP '<'
#define EOP '>'
bool started = false;
bool ended = false;
char inData[80];
byte index;
void setup() {
pinMode(IRledPin, OUTPUT);
Serial.begin(9600);
}
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
char *cmd = strtok(inData, ",");
if (cmd) {
char *val = strtok(NULL, ",");
if (val) {
sendCommand(cmd, val);
}
}
// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
}
void sendCommand(char *command, char *value) {
if (strcmp(command,"power") == 0) {
power(value);
}
}
void power(char* value) {
if (strcmp(value, "led") == 0) {
digitalWrite(IRledPin, HIGH);
}
}
If the 2nd character is the pin, then you want inData[1] for your pin numbers instead of inData[2].
Why do you go from inData[0] to inData[2]? Wouldn't the second character be in inData[1]?
You're setting the pinMode to the actual value of inData[2]. That means to turn on pin 13, you need to send a carriage return character ('\r').
The code doesn't run below but it should help you to sort out your problem.
What it tries to do is split the inData into the Tokens[] array.
It then turns the ASCII data into integers with the atoi() statement.
Hope it helps.
Got the splitter from Segmentation Fault when using strtok_r
bool started = false;
bool ended = false;
char inData[5];
byte index;
void setup()
{
Serial.begin(9600);
}
void loop()
{
while (Serial.available() > 0)
{
char inChar = Serial.read();
if (inChar == '<')
{
index = 0;
started = true;
ended = false;
}
else if (inChar == '>')
{
ended = true;
break;
}
else
{
inData[index] = inChar;
index++;
}
if (started && ended)
{
// https://stackoverflow.com/questions/2227198/segmentation-fault-when-using-strtok-r
// Splint up input data
char *p = inData;
char *tokens[50];
int i = 0;
while (i < 50) {
tokens[i] = strtok_r(p, ",", &p);
if (tokens[i] == NULL) {
break;
}
i++;
}
if (tokens[0] == '<A')
{
pinMode(tokens[1],OUTPUT);
analogWrite(tokens[2],tokens[3]);
}
else if (token[0] == '<D')
{
if (atoi(token[3]) == 1)
{
pinMode(atoi(token[1]),OUTPUT);
digitalWrite(atoi(token[1]),HIGH);
}
else if (atoi(tokens[3]) == 0)
{
pinMode(atoi(tokens[1]),OUTPUT);
digitalWrite(atoi(tokens[1]),LOW);
}
}
started = false;
ended = false;
index = 0;
}
}
I'm using two Arduinos to sent plain text strings to each other using NewSoftSerial and an RF transceiver.
Each string is perhaps 20-30 characters in length. How do I convert Serial.read() into a string so I can do if x == "testing statements", etc.?
Unlimited string readed:
String content = "";
char character;
while(Serial.available()) {
character = Serial.read();
content.concat(character);
}
if (content != "") {
Serial.println(content);
}
From Help with Serial.Read() getting string:
char inData[20]; // Allocate some space for the string
char inChar = -1; // Where to store the character read
byte index = 0; // Index into array; where to store the character
void setup() {
Serial.begin(9600);
Serial.write("Power On");
}
char Comp(char* This) {
while (Serial.available() > 0) // Don't read unless there
// you know there is data
{
if(index < 19) // One less than the size of the array
{
inChar = Serial.read(); // Read a character
inData[index] = inChar; // Store it
index++; // Increment where to write next
inData[index] = '\0'; // Null terminate the string
}
}
if (strcmp(inData, This) == 0) {
for (int i=0; i<19; i++) {
inData[i] = 0;
}
index = 0;
return(0);
}
else {
return(1);
}
}
void loop()
{
if (Comp("m1 on") == 0) {
Serial.write("Motor 1 -> Online\n");
}
if (Comp("m1 off") == 0) {
Serial.write("Motor 1 -> Offline\n");
}
}
You can use Serial.readString() and Serial.readStringUntil() to parse strings from Serial on the Arduino.
You can also use Serial.parseInt() to read integer values from serial.
int x;
String str;
void loop()
{
if(Serial.available() > 0)
{
str = Serial.readStringUntil('\n');
x = Serial.parseInt();
}
}
The value to send over serial would be my string\n5 and the result would be str = "my string" and x = 5
I was asking the same question myself and after some research I found something like that.
It works like a charm for me. I use it to remote control my Arduino.
// Buffer to store incoming commands from serial port
String inData;
void setup() {
Serial.begin(9600);
Serial.println("Serial conection started, waiting for instructions...");
}
void loop() {
while (Serial.available() > 0)
{
char recieved = Serial.read();
inData += recieved;
// Process message when new line character is recieved
if (recieved == '\n')
{
Serial.print("Arduino Received: ");
Serial.print(inData);
// You can put some if and else here to process the message juste like that:
if(inData == "+++\n"){ // DON'T forget to add "\n" at the end of the string.
Serial.println("OK. Press h for help.");
}
inData = ""; // Clear recieved buffer
}
}
}
This would be way easier:
char data [21];
int number_of_bytes_received;
if(Serial.available() > 0)
{
number_of_bytes_received = Serial.readBytesUntil (13,data,20); // read bytes (max. 20) from buffer, untill <CR> (13). store bytes in data. count the bytes recieved.
data[number_of_bytes_received] = 0; // add a 0 terminator to the char array
}
bool result = strcmp (data, "whatever");
// strcmp returns 0; if inputs match.
// http://en.cppreference.com/w/c/string/byte/strcmp
if (result == 0)
{
Serial.println("data matches whatever");
}
else
{
Serial.println("data does not match whatever");
}
The best and most intuitive way is to use serialEvent() callback Arduino defines along with loop() and setup().
I've built a small library a while back that handles message reception, but never had time to opensource it.
This library receives \n terminated lines that represent a command and arbitrary payload, space-separated.
You can tweak it to use your own protocol easily.
First of all, a library, SerialReciever.h:
#ifndef __SERIAL_RECEIVER_H__
#define __SERIAL_RECEIVER_H__
class IncomingCommand {
private:
static boolean hasPayload;
public:
static String command;
static String payload;
static boolean isReady;
static void reset() {
isReady = false;
hasPayload = false;
command = "";
payload = "";
}
static boolean append(char c) {
if (c == '\n') {
isReady = true;
return true;
}
if (c == ' ' && !hasPayload) {
hasPayload = true;
return false;
}
if (hasPayload)
payload += c;
else
command += c;
return false;
}
};
boolean IncomingCommand::isReady = false;
boolean IncomingCommand::hasPayload = false;
String IncomingCommand::command = false;
String IncomingCommand::payload = false;
#endif // #ifndef __SERIAL_RECEIVER_H__
To use it, in your project do this:
#include <SerialReceiver.h>
void setup() {
Serial.begin(115200);
IncomingCommand::reset();
}
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
if (IncomingCommand::append(inChar))
return;
}
}
To use the received commands:
void loop() {
if (!IncomingCommand::isReady) {
delay(10);
return;
}
executeCommand(IncomingCommand::command, IncomingCommand::payload); // I use registry pattern to handle commands, but you are free to do whatever suits your project better.
IncomingCommand::reset();
Here is a more robust implementation that handles abnormal input and race conditions.
It detects unusually long input values and safely discards them. For example, if the source had an error and generated input without the expected terminator; or was malicious.
It ensures the string value is always null terminated (even when buffer size is completely filled).
It waits until the complete value is captured. For example, transmission delays could cause Serial.available() to return zero before the rest of the value finishes arriving.
Does not skip values when multiple values arrive quicker than they can be processed (subject to the limitations of the serial input buffer).
Can handle values that are a prefix of another value (e.g. "abc" and "abcd" can both be read in).
It deliberately uses character arrays instead of the String type, to be more efficient and to avoid memory problems. It also avoids using the readStringUntil() function, to not timeout before the input arrives.
The original question did not say how the variable length strings are defined, but I'll assume they are terminated by a single newline character - which turns this into a line reading problem.
int read_line(char* buffer, int bufsize)
{
for (int index = 0; index < bufsize; index++) {
// Wait until characters are available
while (Serial.available() == 0) {
}
char ch = Serial.read(); // read next character
Serial.print(ch); // echo it back: useful with the serial monitor (optional)
if (ch == '\n') {
buffer[index] = 0; // end of line reached: null terminate string
return index; // success: return length of string (zero if string is empty)
}
buffer[index] = ch; // Append character to buffer
}
// Reached end of buffer, but have not seen the end-of-line yet.
// Discard the rest of the line (safer than returning a partial line).
char ch;
do {
// Wait until characters are available
while (Serial.available() == 0) {
}
ch = Serial.read(); // read next character (and discard it)
Serial.print(ch); // echo it back
} while (ch != '\n');
buffer[0] = 0; // set buffer to empty string even though it should not be used
return -1; // error: return negative one to indicate the input was too long
}
Here is an example of it being used to read commands from the serial monitor:
const int LED_PIN = 13;
const int LINE_BUFFER_SIZE = 80; // max line length is one less than this
void setup() {
pinMode(LED_PIN, OUTPUT);
Serial.begin(9600);
}
void loop() {
Serial.print("> ");
// Read command
char line[LINE_BUFFER_SIZE];
if (read_line(line, sizeof(line)) < 0) {
Serial.println("Error: line too long");
return; // skip command processing and try again on next iteration of loop
}
// Process command
if (strcmp(line, "off") == 0) {
digitalWrite(LED_PIN, LOW);
} else if (strcmp(line, "on") == 0) {
digitalWrite(LED_PIN, HIGH);
} else if (strcmp(line, "") == 0) {
// Empty line: no command
} else {
Serial.print("Error: unknown command: \"");
Serial.print(line);
Serial.println("\" (available commands: \"off\", \"on\")");
}
}
String content = "";
char character;
if(Serial.available() >0){
//reset this variable!
content = "";
//make string from chars
while(Serial.available()>0) {
character = Serial.read();
content.concat(character);
}
//send back
Serial.print("#");
Serial.print(content);
Serial.print("#");
Serial.flush();
}
If you want to read messages from the serial port and you need to deal with every single message separately I suggest separating messages into parts using a separator like this:
String getMessage()
{
String msg=""; //the message starts empty
byte ch; // the character that you use to construct the Message
byte d='#';// the separating symbol
if(Serial.available())// checks if there is a new message;
{
while(Serial.available() && Serial.peek()!=d)// while the message did not finish
{
ch=Serial.read();// get the character
msg+=(char)ch;//add the character to the message
delay(1);//wait for the next character
}
ch=Serial.read();// pop the '#' from the buffer
if(ch==d) // id finished
return msg;
else
return "NA";
}
else
return "NA"; // return "NA" if no message;
}
This way you will get a single message every time you use the function.
Credit for this goes to magma. Great answer, but here it is using c++ style strings instead of c style strings. Some users may find that easier.
String string = "";
char ch; // Where to store the character read
void setup() {
Serial.begin(9600);
Serial.write("Power On");
}
boolean Comp(String par) {
while (Serial.available() > 0) // Don't read unless
// there you know there is data
{
ch = Serial.read(); // Read a character
string += ch; // Add it
}
if (par == string) {
string = "";
return(true);
}
else {
//dont reset string
return(false);
}
}
void loop()
{
if (Comp("m1 on")) {
Serial.write("Motor 1 -> Online\n");
}
if (Comp("m1 off")) {
Serial.write("Motor 1 -> Offline\n");
}
}
If you're using concatenate method then don't forget to trim the string if you're working with if else method.
Use string append operator on the serial.read(). It works better than string.concat()
char r;
string mystring = "";
while(serial.available()){
r = serial.read();
mystring = mystring + r;
}
After you are done saving the stream in a string(mystring, in this case), use SubString functions to extract what you are looking for.
I could get away with this:
void setup() {
Serial.begin(9600);
}
void loop() {
String message = "";
while (Serial.available())
message.concat((char) Serial.read());
if (message != "")
Serial.println(message);
}
Many great answers, here is my 2 cents with exact functionality as requested in the question.
Plus it should be a bit easier to read and debug.
Code is tested up to 128 chars of input.
Tested on Arduino uno r3 (Arduino IDE 1.6.8)
Functionality:
Turns Arduino onboard led (pin 13) on or off using serial command input.
Commands:
LED.ON
LED.OFF
Note: Remember to change baud rate based on your board speed.
// Turns Arduino onboard led (pin 13) on or off using serial command input.
// Pin 13, a LED connected on most Arduino boards.
int const LED = 13;
// Serial Input Variables
int intLoopCounter = 0;
String strSerialInput = "";
// the setup routine runs once when you press reset:
void setup()
{
// initialize the digital pin as an output.
pinMode(LED, OUTPUT);
// initialize serial port
Serial.begin(250000); // CHANGE BAUD RATE based on the board speed.
// initialized
Serial.println("Initialized.");
}
// the loop routine runs over and over again forever:
void loop()
{
// Slow down a bit.
// Note: This may have to be increased for longer strings or increase the iteration in GetPossibleSerialData() function.
delay(1);
CheckAndExecuteSerialCommand();
}
void CheckAndExecuteSerialCommand()
{
//Get Data from Serial
String serialData = GetPossibleSerialData();
bool commandAccepted = false;
if (serialData.startsWith("LED.ON"))
{
commandAccepted = true;
digitalWrite(LED, HIGH); // turn the LED on (HIGH is the voltage level)
}
else if (serialData.startsWith("LED.OFF"))
{
commandAccepted = true;
digitalWrite(LED, LOW); // turn the LED off by making the voltage LOW
}
else if (serialData != "")
{
Serial.println();
Serial.println("*** Command Failed ***");
Serial.println("\t" + serialData);
Serial.println();
Serial.println();
Serial.println("*** Invalid Command ***");
Serial.println();
Serial.println("Try:");
Serial.println("\tLED.ON");
Serial.println("\tLED.OFF");
Serial.println();
}
if (commandAccepted)
{
Serial.println();
Serial.println("*** Command Executed ***");
Serial.println("\t" + serialData);
Serial.println();
}
}
String GetPossibleSerialData()
{
String retVal;
int iteration = 10; // 10 times the time it takes to do the main loop
if (strSerialInput.length() > 0)
{
// Print the retreived string after looping 10(iteration) ex times
if (intLoopCounter > strSerialInput.length() + iteration)
{
retVal = strSerialInput;
strSerialInput = "";
intLoopCounter = 0;
}
intLoopCounter++;
}
return retVal;
}
void serialEvent()
{
while (Serial.available())
{
strSerialInput.concat((char) Serial.read());
}
}
This always works for me :)
String _SerialRead = "";
void setup() {
Serial.begin(9600);
}
void loop() {
while (Serial.available() > 0) //Only run when there is data available
{
_SerialRead += char(Serial.read()); //Here every received char will be
//added to _SerialRead
if (_SerialRead.indexOf("S") > 0) //Checks for the letter S
{
_SerialRead = ""; //Do something then clear the string
}
}
}