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
}
}
}
Related
I am trying to write a Serial read function. That function will give me a data between '#' (start character) and '*' (end character). I tried to write it and it looks like it is kinda work but not perfectly. The problem is that:
I have two arduino. One of these send "MARCO" and other arduino read it. If the readed data is "MARCO" it is write to serial monitor "MARCOCORRECT" else it is write to serial monitor the readed data. Normally it must just write "MARCOCORRECT" because I only send "MARCO" but it don't. It writes something else too. I tried lower baud rate too but it is still same. How can I fix it?
Sender Code
#define BAUD_RATE 38400
void setup()
{
Serial.begin(BAUD_RATE);
}
String readed = "";
void loop()
{
String readed;
while (Serial.available() > 0)
{
readed += Serial.read();
}
Serial.println("#MARCO*");
}
Reader Code
#define BAUD_RATE 38400
#define MSG_START '#'
#define MSG_END '*'
String readed;
char readedChar;
bool msgStart = false;
String serialReadFunc()
{
readedChar = '0';
readed = "";
while (Serial.available() > 0 || msgStart)
{
if (readedChar == MSG_START)
{
msgStart = true;
}
readedChar = (char)Serial.read();
if (readedChar == MSG_END)
{
msgStart = false;
break;
}
if (msgStart)
{
readed += readedChar;
}
}
return readed;
}
void setup()
{
Serial.begin(BAUD_RATE);
}
void loop()
{
if (serialReadFunc() == "MARCO")
{
Serial.println("MARCOCORRECT");
}
else
Serial.println(readed);
}
Console Image On Proteus
Console Image Proteus
I suspect you're having synchronization issues. I may be wrong, though, and I'm unable to test it at the moment.
I'd recommend trying to insert a delay on the sender, like so:
String readed;
while (Serial.available() > 0)
{
readed += Serial.read();
}
delay(10);
Serial.println("#MARCO*");
It would also be interesting to see the return value of the reader Serial.available(). Again, not 100% sure, but I believe the buffer may be full (the buffer holds 64 bytes).
I successfully managed to send a single integer from processing to Arduino but now I want to send an array of three integers and I can't get it working. I want to create a buzzer feedback with Arduino which processing will control which buzzer to activate. For example, the data send from processing should be [1,0,1] meaning sensor 1 and 3 should start working. The buzzers should be able to be activated simultaneously in case that [1,1,1] goes through.
This is the code I have so far:
I am trying to understand what data is being sent back to Arduino to know how to use it and I keep getting either a null value or a random integer.
I'm trying to learn how to do this so apologies if the code is bad.
Arduino
void setup(){
Serial.begin(9600); // Start serial communication at 9600 bps
}
void loop(){
if (Serial.available()){
const char data = Serial.read();
char noteBuzzer[] = {data};
for (int i = 0 ; i < sizeof(noteBuzzer); i++) {
}
Serial.print(noteBuzzer[1]);
}
}
Processing
import processing.serial.*;
String notes[];
String tempo[];
Serial myPort;
String val;
void setup(){
size(200,200);
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
notes = loadStrings("data/notes.txt");
tempo = loadStrings("data/tempo.txt");
}
void draw() {
if (keyPressed == true)
{
if (key == '1') {
println("Start");
readNotes();
}
}
}
void readNotes(){
for (int i = 0 ; i < notes.length; i++) {
println(notes[i]);
//println(tempo[i]);
myPort.write(notes[i]);
delay(int(tempo[i])); //this will be the tempo?
if ( myPort.available() > 0)
{
val = myPort.readStringUntil('\n');
println("Arduino",val);
}
}
}
If you're data is an array that always has 3 items and each of those items are always either 1 or 0 (bits), you could store that whole data in a single byte (and still have 5 more bits to spare). Sending and receiving a byte is pretty simple with Arduino.
Here's a basic sketch that shows you how to flip 3 bits in a single byte:
// the state as a byte
byte buzzerState = 0B000;
void setup(){
textFont(createFont("Courier New",18),18);
}
void draw(){
background(0);
text("DEC: " + buzzerState +
"\nBIN:" + binary(buzzerState,3),10,40);
}
void keyPressed(){
if(key == '1'){
buzzerState = flipBit(buzzerState,0);
}
if(key == '2'){
buzzerState = flipBit(buzzerState,1);
}
if(key == '3'){
buzzerState = flipBit(buzzerState,2);
}
}
// flips a bit at a given index within a byte and returns updated byte
byte flipBit(byte state,int index){
int bit = getBitAt(state,index);
int bitFlipped = 1 - bit;
return setBitAt(state,index,bitFlipped);
}
// returns the integer value of a bit within a byte at the desired index
int getBitAt(byte b,int index){
index = constrain(index,0,7);
return b >> index & 1;
}
// sets an individual bit at a desired index on or off (value) and returns the updated byte
byte setBitAt(byte b,int index, int value){
index = constrain(index,0,7);
value = constrain(value,0,1);
if(value == 1) b |= (1 << (index));
else b &= ~(1 << (index));
return b;
}
Use keys '1','2' and '3' to flip the bits.
Note that in keypress we're always updating the same byte.
The text will display the decimal value first and the binary value bellow.
This is the most efficient way to send your data and the simplest in terms of serial communication. On the Arduino side you can simply use bitRead() on the byte you get from Serial.read(). For more on binary/bits/bytes be sure to read the BitMath Arduino tutorial. Binary may seem intimidating at first, but it's really not that bad once you practice a bit and it's totally worth knowing.
Here's an updated version of the code above that sends the byte to Arduino on the first available serial port (be sure to change Serial.list()[0] with what makes sense for your setup and press 's' to send an update to Arduino:
import processing.serial.*;
// the state as a byte
byte buzzerState = 0B000;
Serial port;
void setup(){
textFont(createFont("Courier New",18),18);
try{
port = new Serial(this,Serial.list()[0],9600);
}catch(Exception e){
e.printStackTrace();
}
}
void draw(){
background(0);
text("DEC: " + buzzerState +
"\nBIN:" + binary(buzzerState,3),10,40);
}
void keyPressed(){
if(key == '1'){
buzzerState = flipBit(buzzerState,0);
}
if(key == '2'){
buzzerState = flipBit(buzzerState,1);
}
if(key == '3'){
buzzerState = flipBit(buzzerState,2);
}
if(key == 's'){
if(port != null){
port.write(buzzerState);
}else{
println("serial port is not open: check port name and cable connection");
}
}
}
// flips a bit at a given index within a byte and returns updated byte
byte flipBit(byte state,int index){
int bit = getBitAt(state,index);
int bitFlipped = 1 - bit;
return setBitAt(state,index,bitFlipped);
}
// returns the integer value of a bit within a byte at the desired index
int getBitAt(byte b,int index){
index = constrain(index,0,7);
return b >> index & 1;
}
// sets an individual bit at a desired index on or off (value) and returns the updated byte
byte setBitAt(byte b,int index, int value){
index = constrain(index,0,7);
value = constrain(value,0,1);
if(value == 1) b |= (1 << (index));
else b &= ~(1 << (index));
return b;
}
And here's a super basic Arduino sketch:
byte buzzerState;
void setup() {
Serial.begin(9600);
//test LEDs setup
pinMode(10,OUTPUT);
pinMode(11,OUTPUT);
pinMode(12,OUTPUT);
}
void loop() {
if(Serial.available() > 0){
buzzerState = Serial.read();
bool bit0 = bitRead(buzzerState,0);
bool bit1 = bitRead(buzzerState,1);
bool bit2 = bitRead(buzzerState,2);
//test LEDs update
digitalWrite(10,bit0);
digitalWrite(11,bit1);
digitalWrite(12,bit2);
}
}
If you connect 3 LEDs to pins 10,11,12 you should them toggle as you press keys '1','2','3' then 's' in Processing
One way around binary in Processing could be using a String representation of your data (e.g. "00000101" for [1,0,1]) and unbinary() to convert that String to an integer value you can write to serial, but it will be a bit annoying to getting and setting a character at an index (and potentially parsing that char to it's integer value and back)
When you need to send more than a byte things get a bit more complicated as you need to handle data corruption/interruptions, etc. In these situations it's best to setup/design a communication protocol based on your needs and this isn't easy if you're just getting started with Arduino, but not impossible either. Here's an example, there are many more online.
One quick and dirty thing you could try is sending that data as string terminated by a new line character (\n) which you could buffer until in Arduino then read 4 bytes at a time, discarding the \n when parsing:
e.g. sending "101\n" from Processing, representing [1,0,1] then on the Arduino side use Serial.readStringUntil('\n') and a combination of charAt() and toInt() to access each integer within that that string.
Here's an example Processing sketch:
import processing.serial.*;
// the state as a byte
String buzzerState = "010\n";
Serial port;
void setup(){
textFont(createFont("Courier New",18),18);
try{
port = new Serial(this,Serial.list()[0],9600);
}catch(Exception e){
e.printStackTrace();
}
}
void draw(){
background(0);
text(buzzerState,30,50);
}
void keyPressed(){
if(key == '1'){
buzzerState = flipBit(buzzerState,0);
}
if(key == '2'){
buzzerState = flipBit(buzzerState,1);
}
if(key == '3'){
buzzerState = flipBit(buzzerState,2);
}
if(key == 's'){
if(port != null){
port.write(buzzerState);
}else{
println("serial port is not open: check port name and cable connection");
}
}
}
String flipBit(String state,int index){
index = constrain(index,0,2);
// parse integer from string
int bitAtIndex = Integer.parseInt(state.substring(index,index+1));
// return new string concatenating the prefix (if any), the flipped bit (1 - bit) and the suffix
return state = (index > 0 ? state.substring(0,index) : "") + (1 - bitAtIndex) + state.substring(index+1);
}
And an Arduino one based on Arduino > File > Examples > 04.Communication > SerialEvent:
/*
Serial Event example
When new serial data arrives, this sketch adds it to a String.
When a newline is received, the loop prints the string and
clears it.
A good test for this is to try it with a GPS receiver
that sends out NMEA 0183 sentences.
Created 9 May 2011
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/SerialEvent
*/
String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
void setup() {
// initialize serial:
Serial.begin(9600);
// reserve 200 bytes for the inputString:
inputString.reserve(200);
// test LEDs setup
pinMode(10,OUTPUT);
pinMode(11,OUTPUT);
pinMode(12,OUTPUT);
}
void loop() {
// print the string when a newline arrives:
if (stringComplete) {
Serial.println(inputString);
// process string
bool bit0 = inputString.charAt(2) == '1';
bool bit1 = inputString.charAt(1) == '1';
bool bit2 = inputString.charAt(0) == '1';
//test LEDs update
digitalWrite(10,bit0);
digitalWrite(11,bit1);
digitalWrite(12,bit2);
// clear the string:
inputString = "";
stringComplete = false;
}
}
/*
SerialEvent occurs whenever a new data comes in the
hardware serial RX. This routine is run between each
time loop() runs, so using delay inside loop can delay
response. Multiple bytes of data may be available.
*/
void serialEvent() {
while (Serial.available()) {
// get the new byte:
char inChar = (char)Serial.read();
// add it to the inputString:
inputString += inChar;
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inChar == '\n') {
stringComplete = true;
}
}
}
Note this is more prone to error and used 4 times as much data as the the single byte option.
I am using an Arduino mega2560 and an EZO EC(Electrical Conductivity) and am trying to send a command using the Serial.print() function. I am using the Arduino IDE 1.6.7.
I have some code that seems to work fine which I found online. But I want to know why my code will not work. The EC sensor does not seem to get the data I am sending. No data seems to being sent.
I know it is not my connection as I have tested the setup with the code that works it runs fine and as expected.
Here is the code I found online that works:
String inputstring = "";
String sensorstring = "";
boolean input_string_complete = false;
boolean sensor_string_complete = false;
void setup() {
Serial.begin(9600);
Serial3.begin(9600);
inputstring.reserve(10);
sensorstring.reserve(30);
}
void serialEvent() {
inputstring = Serial.readStringUntil(13);
input_string_complete = true;
}
void serialEvent3() {
sensorstring = Serial3.readStringUntil(13);
sensor_string_complete = true;
}
void loop() {
float wt = 28.9;
String tempCal = "T,";
tempCal += wt;
if (input_string_complete == true) {
Serial3.print(inputstring);
Serial3.print("\r");
inputstring = "";
input_string_complete = false;
}
if (sensor_string_complete == true) {
if (isdigit(sensorstring[0]) == false) {
Serial.println(sensorstring);
}
else
print_EC_data();
}
sensorstring = "";
sensor_string_complete = false;
}
}
void print_EC_data(void) {
char sensorstring_array[30];
char *EC;
char *TDS;
char *SAL;
char *GRAV;
float f_ec;
sensorstring.toCharArray(sensorstring_array, 30);
EC = strtok(sensorstring_array, ",");
TDS = strtok(NULL, ",");
SAL = strtok(NULL, ",");
GRAV = strtok(NULL, ",");
Serial.print("EC:");
Serial.println(EC);
Serial.print("TDS:");
Serial.println(TDS);
Serial.print("SAL:");
Serial.println(SAL);
Serial.print("GRAV:");
Serial.println(GRAV);
Serial.println();
//f_ec= atof(EC);
}
Here is my code:
void setup() {
Serial.begin(9600);
Serial3.print(9600);
}
void loop() {
Serial3.print("R/r");
Serial.print("R/r");
delay(2000);
}
The Serial3.print just doesn't get sent to the sensor. But the other code also sends a string using the Serial3.print() function an it works fine. I do not know what I am doing wrong.
I understand that I need to write a procedure to take in anything that comes in from the sensor. But nothing seems to be even sent to the sensor in the first place!
Any help would be greatly appreciated. Thank you
You're using slash, not backslash. Change this
Serial3.print("R/r");
to this:
Serial3.print("R\r");
And don't use the String class. It'll mess you up. :) Just use char arrays, and fill them up as characters are available in loop. When the \r finally arrives, process the array:
char inputString[16];
int inputStringIndex = 0;
void loop()
{
if (Serial.available()) {
char c = Serial.read();
if (c == '\r') {
inputString[ inputStringIndex ] = '\0'; // NUL-terminate string
inputStringIndex = 0; // reset for next time
Serial3.print( inputString );
Serial3.print( '\r' );
} else if (inputStringIndex < sizeof(inputString)-1) {
inputString[ inputStringIndex++ ] = c;
}
}
Do something similar for the response line. This will also avoid blocking inside a SerialEvent. :P
You have an error in your setup() block: before you can send data over a serial connection you need to configure the connection with a begin() statement:
Serial3.begin(9600)
But in the code in your question you have
Serial3.print(9600)
And make sure that you have the EZO connected to the correct pins for Serial3 (14,15).
Also you need to use the "\" for printing control characters.
I'm trying to get a raspberry pi communicating with an arduino over serial, hardware all is setup and there are some comms ok.
Problem I have is the arguments passed are not matched with a if, else if expression but really should be going off what is returned.
SerialCommand library handles what commands trigger what functions. We're only interested in sayHello() for this example. RaspberryPi sends "HELLO HELLO", which should receive "YES HELLO" but I always get "NO HELLO" suggesting no match on the arg HELLO. Obviously what argument was received is echoed back regardless and I receive back what should have matched, HELLO so I'm stumped at this stage, any suggestions?
EDIT: THanks to Thanushan Balakrishnan comment in his answer below, replacing
if(arg == "HELLO")
with the below fixes the problem.
if(strcmp("HELLO", arg) == 0)
_
// Demo Code for SerialCommand Library
// Steven Cogswell
// May 2011
// temp & Humidity
#include <DHT22.h>
#define DHT22_PIN 12
// Setup a DHT22 instance
DHT22 myDHT22(DHT22_PIN);
// tank water level
int sensorValue = 0;
int constrainedValue = 0;
int tankLevel = 0;
#define TANK_SENSOR 0
#define TANK_EMPTY 0
#define TANK_FULL 1023
/*RelayBrd */
//Pin connected to latch pin (ST_CP) of 74HC595
const int latchPin = 4;
//Pin connected to clock pin (SH_CP) of 74HC595
const int clockPin = 3;
////Pin connected to Data in (DS) of 74HC595
const int dataPin = 2;
boolean thisState = LOW;
#include <SerialCommand.h>
#define arduinoLED 13 // Arduino LED on board
SerialCommand sCmd; // The demo SerialCommand object
void setup() {
pinMode(arduinoLED, OUTPUT); // Configure the onboard LED for output
digitalWrite(arduinoLED, LOW); // default to LED off
//mySerial.begin(9600);
//mySerial.println("Ready");
Serial.begin(115200);
// Setup callbacks for SerialCommand commands
sCmd.addCommand("ON", LED_on); // Turns LED on
sCmd.addCommand("OFF", LED_off); // Turns LED off
sCmd.addCommand("getenv", getEnv);
sCmd.addCommand("relaybrd", relayBrd);
sCmd.addCommand("gettanklevel", getTankLevel);
sCmd.addCommand("setlouver", setLouver);
sCmd.addCommand("HELLO", sayHello); // Echos the string argument back
sCmd.addCommand("P", processCommand); // Converts two arguments to integers and echos them back
sCmd.setDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?")
Serial.println("Ready");
}
void loop() {
sCmd.readSerial(); // We don't do much, just process serial commands
// if (mySerial.available())
// Serial.write(mySerial.read());
// if (Serial.available())
// mySerial.write(Serial.read());
}
void relayBrd(){
char *arg;
char *arg1;
arg = sCmd.next();
arg1 = sCmd.next();
if (arg != NULL) {
//int num = atol(arg);
if (arg1 != NULL) {
int state = atol(arg1);
if (arg == "fan") {
//do something when var equals 1
registerWrite(1, state);
}else if(arg == "water"){
//do something when var equals 2
registerWrite(2, state);
}else if(arg == "mister"){
//do something when var equals 2
registerWrite(3, state);
}else if(arg == "heater"){
//do something when var equals 2
registerWrite(4, state);
}else{
// if nothing else matches, do the default
Serial.print("ERR got:");Serial.print(arg);Serial.println(":");
}
}else{
Serial.println("ERR Relay state missing 1/0");
}
}else{
Serial.println("ERR Relay argument missing fan/water/heater/mister");
}
}
// This method sends bits to the shift register:
void registerWrite(int whichPin, int whichState) {
Serial.print("Relay ");Serial.print(whichPin);Serial.print(" set to ");Serial.println(whichState);
// the bits you want to send
byte bitsToSend = 0;
// turn off the output so the pins don't light up
// while you're shifting bits:
digitalWrite(latchPin, LOW);
// turn on the next highest bit in bitsToSend:
bitWrite(bitsToSend, whichPin, whichState);
// shift the bits out:
shiftOut(dataPin, clockPin, MSBFIRST, bitsToSend);
// turn on the output so the LEDs can light up:
digitalWrite(latchPin, HIGH);
}
void getEnv(){
Serial.println("26 degC");
DHT22_ERROR_t errorCode;
Serial.print("Requesting data...");
errorCode = myDHT22.readData();
switch(errorCode)
{
case DHT_ERROR_NONE:
Serial.print("Got Data ");
Serial.print(myDHT22.getTemperatureC());
Serial.print("C ");
Serial.print(myDHT22.getHumidity());
Serial.println("%");
break;
default:
Serial.print("ERR DHT22 ");
Serial.println(errorCode);
}
}
void getTankLevel(){
Serial.println("78% water level");
sensorValue = analogRead( TANK_SENSOR );
constrainedValue = constrain( sensorValue, TANK_EMPTY, TANK_FULL );
tankLevel = map( constrainedValue, TANK_EMPTY, TANK_FULL, 0, 100 );
}
void setLouver() {
char *arg;
arg = sCmd.next();
if (arg != NULL) {
if(arg == "OPEN"){
Serial.println("Louver OPEN");
}else if(arg == "CLOSE"){
Serial.println("Louver CLOSED");
}else{
Serial.print(arg);Serial.println(" not known");
}
}else{
Serial.println("Louver command missing OPEN/CLOSE");
}
}
void LED_on() {
Serial.println("LED on");
digitalWrite(arduinoLED, HIGH);
}
void LED_off() {
Serial.println("LED off");
digitalWrite(arduinoLED, LOW);
}
void sayHello() {
char *arg;
arg = sCmd.next(); // Get the next argument from the SerialCommand object buffer
if (arg != NULL) { // As long as it existed, take it
if (arg == "HELLO") { // As long as it existed, take it
Serial.print("YES ");
Serial.println(arg);
}else{
Serial.print("NO ");
Serial.println(arg);
}
}
else {
Serial.println("Hello Pi");
}
}
void processCommand() {
int aNumber;
char *arg;
Serial.println("We're in processCommand");
arg = sCmd.next();
if (arg != NULL) {
aNumber = atoi(arg); // Converts a char string to an integer
Serial.print("First argument was: ");
Serial.println(aNumber);
}
else {
Serial.println("No arguments");
}
arg = sCmd.next();
if (arg != NULL) {
aNumber = atol(arg);
Serial.print("Second argument was: ");
Serial.println(aNumber);
}
else {
Serial.println("No second argument");
}
}
// This gets set as the default handler, and gets called when no other command matches.
void unrecognized(const char *command) {
Serial.println("What?");
}
In the following function arg is a pointer. So arg has the address of the memory which holds "HELLO". so you should check *arg == "HELLO"
void sayHello()
{
char *arg;
arg = sCmd.next(); // Get the next argument from the SerialCommand object buffer
if (arg != NULL) { // As long as it existed, take it
if (strcmp("HELLO\n", arg) == 0) { <-------------------------- HERE
Serial.print("YES ");
Serial.println(arg);
}else{
Serial.print("NO ");
Serial.println(arg);
}
}
else {
Serial.println("Hello Pi");
}
}
Consider:
String cmdData; // Store the complete command on one line to send to sensor board.
String phResponse; // Store the pH sensor response.
boolean startOfLine = false;
boolean endOfLine = false;
boolean stringComplete = false;
void setup()
{
Serial.begin(38400);
Serial3.begin(38400);
pinMode(2, OUTPUT); // Used for temperature probe
}
void loop()
{
if (stringComplete)
{
Serial.println("Stored Response: " + phResponse);
phResponse = ""; // Empty phResponse variable to get
// ready for the next response
stringComplete = false;
}
}
void serialEvent()
{
while (Serial.available())
{
char cmd = (char)Serial.read();
if (cmd == '{')
{
startOfLine = true;
}
if (cmd == '}')
{
endOfLine = true;
}
if (startOfLine && cmd != '{' && cmd != '}')
{
cmdData += cmd;
}
if (startOfLine && endOfLine)
{
startOfLine = false;
endOfLine = false;
cmdData.toLowerCase(); // Convert cmdData value to lowercase
// for sanity reasons
if (cmdData == "ph")
{
delay(500);
ph();
}
if (cmdData == "phatc")
{
delay(500);
phATC();
}
cmdData = ""; // Empty cmdData variable to get ready for the next command
}
}
}
void serialEvent3()
{
while(Serial3.available())
{
char cmd3 = (char)Serial3.read();
phResponse += String(cmd3);
if (cmd3 == '\r')
{
stringComplete = true;
Serial.println("Carriage Command Found!");
}
}
}
float getTemp(char tempType)
{
float v_out; // Voltage output from temperature sensor
float temp; // The final temperature is stored here (this is only for code clarity)
float tempInCelcius; // Stores temperature in °C
float tempInFarenheit; // Stores temperature in °F
digitalWrite(A0, LOW); // Set pull-up resistor on analog pin
digitalWrite(2, HIGH); // Set pin 2 high, this will turn on temperature sensor
delay(2); // Wait 1 ms for temperature to stabilize
v_out = analogRead(0); // Read the input pin
digitalWrite(2, LOW); // Set pin 2 low, this will turn off temperature sensor
v_out *=. 0048; // Convert ADC points to voltage [volts] (we are using .0048
// because this device is running at 5 volts)
v_out *= 1000; // Convert unit from volts to millivolts
tempInCelcius = 0.0512 * v_out -20.5128; // The equation from millivolts to temperature
tempInFarenheit = (tempInCelcius * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit
if (tempType == 'c')
{
return tempInCelcius; // Return temperature in Celsius
}
else if (tempType == 'f')
{
return tempInFarenheit; // Return temperature in Fahrenheit
}
}
void ph()
{
Serial.println("Calculating pH sensor value in 3 seconds");
delay(3000);
Serial3.print("r\r");
}
void phATC()
{
Serial.println("pH auto temperature calibration will start in 3 seconds");
delay(3000);
float temp = getTemp('c');
char tempAr[10];
String tempAsString;
String tempData;
dtostrf(temp, 1, 2, tempAr);
tempAsString = String(tempAr);
tempData = tempAsString + '\r';
Serial3.print(tempData);
}
Why does serialEvent3() trigger after the second and sometimes the third time a command is sent to the sensor board? Once serialEvent3() finally triggers the consecutive commands work fluently. serialEvent() seems to work as expected. I have tried rearranging the functions without success. Is there a 'fail safe' time-out code to send the command again if serialEvent3 is not triggered?
Working code:
String cmdData; // Store the complete command on one line to send to sensor board.
String phResponse; // Store the pH sensor response.
boolean startOfLine = false;
boolean endOfLine = false;
boolean stringComplete = false;
boolean s3Trigger = false;
void setup()
{
Serial3.begin(38400);
Serial.begin(38400);
}
void serialEvent()
{
while (Serial.available())
{
char cmd = (char)Serial.read();
if (cmd == '{')
{
startOfLine = true;
}
if (cmd == '}')
{
endOfLine = true;
}
if (startOfLine && cmd != '{' && cmd != '}')
{
cmdData += cmd;
//Serial.println(cmdData);
}
}
}
void serialEvent3()
{
while(Serial3.available())
{
char cmd3 = (char)Serial3.read();
phResponse += String(cmd3);
if (cmd3 == '\r') // If carriage return has been found then...
{
stringComplete = true;
}
}
}
void loop()
{
if (startOfLine && endOfLine) // Both startOfLine and endOfLine must
// be true to run the command...
{
startOfLine = false;
endOfLine = false;
s3Trigger = true; // Set the s3Trigger boolean to true to check
// if data on Serial3.available() is available.
runCommand();
}
if (stringComplete)
{
Serial.println("Stored Response: " + phResponse); // Print stored response from the pH sensor.
phResponse = ""; // Empty phResponse variable to get ready for the next response
cmdData = ""; // Empty phResponse variable to get ready for the next command
stringComplete = false; // Set stringComplete to false
s3Trigger = false; // Set s3Trigger to false so it doesn't continuously loop.
}
if (s3Trigger) // If true then continue
{
delay(1000); // Delay to make sure the Serial3 buffer has all the data
if (!Serial3.available()) // If Serial3 available then execute
// the runCommand() function
{
//Serial.println("!Serial3.available()");
runCommand();
}
else
{
s3Trigger = false; // Set s3Trigger to false so it doesn't continuously loop.
}
}
}
void runCommand()
{
cmdData.toLowerCase(); // Convert cmdData value to lowercase
// for sanity reasons
if (cmdData == "ph")
{
ph();
}
}
void ph()
{
Serial.println("Calculating pH sensor value in 3 seconds");
delay(3000);
Serial3.print("r\r");
}
New working code without having to send the command twice:
/*
This software was made to demonstrate how to quickly get your
Atlas Scientific product running on the Arduino platform.
An Arduino MEGA 2560 board was used to test this code.
This code was written in the Arudino 1.0 IDE
Modify the code to fit your system.
**Type in a command in the serial monitor and the Atlas
Scientific product will respond.**
**The data from the Atlas Scientific product will come out
on the serial monitor.**
Code efficacy was NOT considered, this is a demo only.
The TX3 line goes to the RX pin of your product.
The RX3 line goes to the TX pin of your product.
Make sure you also connect to power and GND pins to power
and a common ground.
Open TOOLS > serial monitor, set the serial monitor
to the correct serial port and set the baud rate to 38400.
Remember, select carriage return from the drop down menu
next to the baud rate selection; not "both NL & CR".
*/
String inputstring = ""; // A string to hold incoming data from the PC
String sensorstring = ""; // A string to hold the data
// from the Atlas Scientific product
boolean input_stringcomplete = false; // Have we received all
// the data from the PC
boolean sensor_stringcomplete = false; // Have we received all the data from
// the Atlas Scientific product
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11); // RX, TX
void setup() { // Set up the hardware
// Set up the hardware
Serial.begin(38400); // Set baud rate for the hardware
// serial port_0 to 38400
mySerial.begin(38400);
inputstring.reserve(5); // Set aside some bytes for
// receiving data from the PC
sensorstring.reserve(30); // Set aside some bytes for receiving
// data from Atlas Scientific product
pinMode(12, OUTPUT);
digitalWrite(12, HIGH); // Turn on pull-up resistors
//mySerial.print("i\r");
}
void serialEvent() { // If the hardware serial port_0 receives a char
char inchar = (char)Serial.read(); // Get the char we just received
inputstring += inchar; // Add it to the inputString
if(inchar == '\r') { // If the incoming character is a <CR>, set the flag
input_stringcomplete = true;
}
}
void loop() { // Here we go....
while(mySerial.available())
{
char inchar = (char)mySerial.read(); // Get the char we just received
sensorstring += inchar; // Add it to the inputString
if(inchar == '\r') { // If the incoming character
// is a <CR>, set the flag
sensor_stringcomplete = true;
}
//Serial.print(inchar);
}
if (input_stringcomplete){ // If a string from the PC has been
// received in its entirety
//Serial.print(inputstring);
mySerial.print(inputstring); // Send that string to the Atlas Scientific product
inputstring = ""; // Clear the string:
input_stringcomplete = false; // Reset the flag used to tell if we have
// received a completed string from the PC
}
if (sensor_stringcomplete) { // If a string from the Atlas Scientific
// product has been received in its entirety
Serial.println(sensorstring); // Send that string to to the PC's serial monitor
sensorstring = ""; // Clear the string:
sensor_stringcomplete = false; // Reset the flag used to tell if
// we have received a completed string
// from the Atlas Scientific product
}
}
One thing I noticed (not the cause of the serial read problem). In a microprocessor, floating point math is expensive. It might be worth your while to combine these three lines:
v_out*=.0048;
v_out*=1000;
tempInCelcius = 0.0512 * v_out -20.5128;
into:
tempInCelcius = v_out * 0.24576 -20.2128;
I would also move tempInFarenheit = (tempInCelcius * 9.0)/ 5.0 + 32.0; right into the return statement so the calculation is only done if required. Generally splitting these lines up is encouraged for programming on a PC, but with a microprocessor I tend to compact my code and insert lots of comments.
I had a look at the example code for the sensor and I think I have something you can try. You have a while (Serial.available()) line in here. I can't be certain, but this doesn't look right to me and it might be messing you up. Try removing that block and just doing the read in the event.