Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« on: March 30, 2012, 09:51:39 pm » |
This question must come up every day, so here goes ... To send a number (greater than 0 to 9) from one Arduino to another reliably you need to do a couple of things. - Send some sort of "start number" delimiter (in this example, the "<" character).
- "Print" the number, which turns it into a string of ASCII characters.
- Send some sort of "end number" delimiter (in this example, the ">" character).
Example sketch, sending 10 random numbers out the serial port: // Example of sending numbers by Serial // Author: Nick Gammon // Date: 31 March 2012
const char startOfNumberDelimiter = '<'; const char endOfNumberDelimiter = '>';
void setup () { srand (42); Serial.begin (115200); } // end of setup void loop () { Serial.println ("Starting ..."); for (int i = 0; i < 10; i++) { Serial.print (startOfNumberDelimiter); Serial.print (rand ()); // send the number Serial.print (endOfNumberDelimiter); Serial.println (); } // end of for
delay (5000); } // end of loop Output: Starting ... <17766> <11151> <23481> <32503> <7018> <25817> <28529> <9160> <16666> <13513>
To receive the numbers we need to detect in our main loop whether or not anything has arrived in the serial port. If so, we can check for the special "start" and "end" characters. These tell us whether to start a new number, and when it has finished arriving. // Example of receiving numbers by Serial // Author: Nick Gammon // Date: 31 March 2012
const char startOfNumberDelimiter = '<'; const char endOfNumberDelimiter = '>';
void setup () { Serial.begin (115200); Serial.println ("Starting ..."); } // end of setup void processNumber (const long n) { Serial.println (n); } // end of processNumber void processInput () { static long receivedNumber = 0; static boolean negative = false; byte c = Serial.read (); switch (c) { case endOfNumberDelimiter: if (negative) processNumber (- receivedNumber); else processNumber (receivedNumber);
// fall through to start a new number case startOfNumberDelimiter: receivedNumber = 0; negative = false; break; case '0' ... '9': receivedNumber *= 10; receivedNumber += c - '0'; break; case '-': negative = true; break; } // end of switch } // end of processInput void loop () { if (Serial.available ()) processInput (); // do other stuff here } // end of loop Output: Starting ... 17766 11151 23481 32503 7018 25817 28529 9160 16666 13513 You will note that the receiving sketch does not have any delay calls in it. It runs at full speed, detecting incoming numbers as fast as it can. When a complete number has arrived it is processed (in processNumber). You can choose different delimiters, just make sure that the sending and the receiving end use the same ones. The receiving sketch is designed so that you can omit the starting delimiter between numbers, as an ending delimiter is assumed to start a new number.
More tips about handling serial communications here: http://www.gammon.com.au/serial
|
|
|
|
« Last Edit: March 30, 2012, 10:03:49 pm by Nick Gammon »
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #1 on: March 30, 2012, 09:55:39 pm » |
There are other ways of doing it (eg. by sending raw data) but this is probably the easiest to understand, and has the advantage that the start/end number delimiters keep the sending and receiving end in sync.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 9
|
 |
« Reply #2 on: October 07, 2012, 04:53:27 pm » |
thanks! this algorithm was very useful!
Is there a way to save each number in a different variable on the receiver-Arduino ? I'm trying to do that but just can't get it done.
thanks in advance!
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #3 on: October 07, 2012, 05:21:54 pm » |
Sure. Maybe post what you have? (Your code).
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 9
|
 |
« Reply #4 on: October 07, 2012, 05:38:14 pm » |
Lets see, i have two arduinos connected through xbee, from one i have a sensor RHT03 and a dust sensor sharp. The thing is i must send the data using serial like this: #include <DHT22.h> #include <stdio.h> #include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
int dustValenvio = 0; int dustPin=3; int dustVal=0; int ledPower=2; int delayTime=280; int delayTime2=40; float offTime=9680;
// Cable de datos conectado en pin 4 de Arduino
#define DHT22_PIN 4
// Inicio de Setup DHT22 DHT22 myDHT22(DHT22_PIN);
const char inicionumero = '<'; const char finnumero = '>';
float enviodatosT = 0; float enviodatosH = 0;
void setup(void) { Serial.begin(9600); lcd.begin(16, 2); pinMode(ledPower,OUTPUT);
}
void loop(void) { LCD(); valorsensor(); DHT22_ERROR_t errorCode; // El sensor solo puede leer cada 2 segundos y requiere un tiempo de inico de 2 segundos al menos. delay(3000); errorCode = myDHT22.readData(); switch(errorCode) { case DHT_ERROR_NONE: enviodatosT = myDHT22.getTemperatureC()/1.0; enviodatosH = myDHT22.getHumidity()/1.0; break; case DHT_BUS_HUNG: Serial.println("BUS Hung "); break; case DHT_ERROR_NOT_PRESENT: Serial.println("Not Present "); break; case DHT_ERROR_ACK_TOO_LONG: Serial.println("ACK time out "); break; case DHT_ERROR_SYNC_TIMEOUT: Serial.println("Sync Timeout "); break; case DHT_ERROR_DATA_TIMEOUT: Serial.println("Data Timeout "); break; case DHT_ERROR_TOOQUICK: break; } Serial.print(inicionumero); Serial.print(enviodatosT); Serial.print(finnumero); Serial.println(); Serial.print(inicionumero); Serial.print(enviodatosH); Serial.print(finnumero); Serial.println(); Serial.print(inicionumero); Serial.print(dustValenvio); Serial.print(finnumero); Serial.println();
}
void LCD() { lcd.setCursor(0, 0); lcd.print("T/H "); lcd.print(enviodatosT); lcd.print(" "); lcd.print(enviodatosH); lcd.setCursor(0, 1); lcd.print(dustVal); lcd.print(" "); }
void valorsensor() { digitalWrite(ledPower,LOW); // power on the LED delayMicroseconds(delayTime); dustVal=analogRead(dustPin); // read the dust value via pin 5 on the sensor delayMicroseconds(delayTime2); digitalWrite(ledPower,HIGH); // turn the LED off delayMicroseconds(offTime); dustValenvio=dustVal*100; }
And the receiver Arduino has this code (using your algorithm): const char inicionumero = '<'; const char finnumero = '>';
void setup () { Serial.begin (9600); } void processNumber (const long n) { float x = n/100.0; Serial.println (x); } void processInput () { static float receivedNumber = 0; static boolean negative = false; byte c = Serial.read (); switch (c) { case finnumero: if (negative) processNumber (- receivedNumber); else processNumber (receivedNumber);
// fall through to start a new number case inicionumero: receivedNumber = 0; negative = false; break; case '0' ... '9': receivedNumber *= 10; receivedNumber += c - '0'; break; case '-': negative = true; break; } } void loop () { if (Serial.available ()) processInput (); }
So my problem is, when i receive the three numbers in the monitor serial (temperature, humidity and dust) your algorithm prints the three numbers fine, but i would like to separate one variable for each type of data, i mean temperature, humidity and dust as well, all of that because the code later on storage the data on a microSD card... sorry for my bad english, im a Chilean engineering student.
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #5 on: October 07, 2012, 05:51:30 pm » |
Well, keep track of which is which. eg. const char inicionumero = '<'; const char finnumero = '>';
enum { getTemperature, getHumidity, getDust };
int whichNumber = getTemperature;
float temperature, humidity, dust;
void setup () { Serial.begin (9600); } void processNumber (const long n) { float x = n/100.0; switch (whichNumber) { case getTemperature: temperature = x; whichNumber = getHumidity; Serial.print ("Temperature = "); break; case getHumidity: humidity = x; whichNumber = getDust; Serial.print ("Humidity = "); break;
case getDust: dust = x; whichNumber = getTemperature; Serial.print ("Dust = "); break; } Serial.println (x); } void processInput () { static float receivedNumber = 0; static boolean negative = false; byte c = Serial.read (); switch (c) { case finnumero: if (negative) processNumber (- receivedNumber); else processNumber (receivedNumber);
// fall through to start a new number case inicionumero: receivedNumber = 0; negative = false; break; case '0' ... '9': receivedNumber *= 10; receivedNumber += c - '0'; break; case '-': negative = true; break; } } void loop () { if (Serial.available ()) processInput (); }
That's one way you could do it.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 9
|
 |
« Reply #6 on: October 07, 2012, 06:14:48 pm » |
well, again Mr. Nick, thanks! works like a charm! now i can continue with my project, i was stucked for several days figuring a way how to do this. I'm very grateful
thanks again
Sincerely
Ignacio.
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #7 on: November 17, 2012, 11:37:41 pm » |
I don't know what I (or anyone else) is supposed to answer without seeing any code.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 9
|
 |
« Reply #8 on: November 17, 2012, 11:48:53 pm » |
The code for sending the numbers is #include <DHT22.h> #include <stdio.h> #include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
int dustValenvio = 0; int dustPin=0; int dustVal=0; int ledPower=2; int delayTime=280; int delayTime2=40; float offTime=9680;
// Cable de datos conectado en pin 4 de Arduino
#define DHT22_PIN 4
// Inicio de Setup DHT22 DHT22 myDHT22(DHT22_PIN);
const char inicionumero = '<'; const char finnumero = '>';
float enviodatosT = 0; float enviodatosH = 0;
void setup(void) { Serial.begin(9600); lcd.begin(16, 2); pinMode(ledPower,OUTPUT); }
void loop(void) {
valorsensor(); DHT22_ERROR_t errorCode; errorCode = myDHT22.readData(); switch(errorCode) { case DHT_ERROR_NONE: enviodatosT = myDHT22.getTemperatureC()/1.0; enviodatosH = myDHT22.getHumidity()/1.0; break; case DHT_BUS_HUNG: Serial.println("BUS Hung "); break; case DHT_ERROR_NOT_PRESENT: Serial.println("Not Present "); break; case DHT_ERROR_ACK_TOO_LONG: Serial.println("ACK time out "); break; case DHT_ERROR_SYNC_TIMEOUT: Serial.println("Sync Timeout "); break; case DHT_ERROR_DATA_TIMEOUT: Serial.println("Data Timeout "); break; case DHT_ERROR_TOOQUICK: break; } if(enviodatosT>0) { LCD(); Serial.print(inicionumero); Serial.print(enviodatosT); Serial.print(finnumero); Serial.println(); Serial.print(inicionumero); Serial.print(enviodatosH); Serial.print(finnumero); Serial.println(); Serial.print(inicionumero); Serial.print(dustValenvio); Serial.print(finnumero); Serial.println(); } delay(2000); }
void LCD() { if(enviodatosT>0) { lcd.setCursor(0, 0); lcd.print("T/H "); lcd.print(enviodatosT); lcd.print(" "); lcd.print(enviodatosH); lcd.setCursor(0, 1); lcd.print(dustVal); lcd.print(" "); } }
void valorsensor() { digitalWrite(ledPower,LOW); // power on the LED delayMicroseconds(delayTime); dustVal=analogRead(dustPin); // read the dust value via pin 5 on the sensor delayMicroseconds(delayTime2); digitalWrite(ledPower,HIGH); // turn the LED off delayMicroseconds(offTime); dustValenvio=dustVal; }
The sending number code works very well, but when i receive the numbers it's strange because it keeps showing the same old numbers all over. this is the code for the receiver: #include <SD.h> #include <Wire.h> #include "Rtc.h" #include <DHT22.h> #include <stdio.h>
enum { getTemperature, getHumedad, getDust };
int whichNumber = getTemperature; float temperature, humidity, dust; const char inicionumero = '<'; const char finnumero = '>'; char temperatura[10]; char humedad[10]; char polvo[10]; char temperatura2[10]; char humedad2[10]; char polvo2[10];
float dustValenvio = 0.0; float a = 0.0; int dustPin=0; float dustVal=0.0; int ledPower=2; int delayTime=280; int delayTime2=40; float offTime=9680;
// Cable de datos conectado en pin 4 de Arduino
#define DHT22_PIN 4
// Inicio de Setup DHT22 DHT22 myDHT22(DHT22_PIN);
float enviodatosT = 0; float enviodatosH = 0;
const int chipSelect = 8;
char time[20]; char date[20];
Rtc rtc;
void setup() { Serial.begin(9600); pinMode(ledPower,OUTPUT); pinMode(10, OUTPUT); // see if the card is present and can be initialized: if (!SD.begin(chipSelect)) { // Serial.println("FAILED"); // don't do anything more: return; } Rtc rtc = Rtc(0,0,0,1,1,1,11,0xAA); }
void processNumber (const long n) { float x = n/100.0; switch (whichNumber) { case getTemperature: temperature = x; whichNumber = getHumedad; break; case getHumedad: humidity = x; whichNumber = getDust; break;
case getDust: dust = x; whichNumber = getTemperature; break; } dtostrf(temperature,1,2,temperatura2); dtostrf(humidity,1,2,humedad2); dtostrf(dust,1,2,polvo2); } void processInput () { static float receivedNumber = 0; static boolean negative = false; byte c = Serial.read (); switch (c) { case finnumero: if (negative) processNumber (- receivedNumber); else processNumber (receivedNumber);
// fall through to start a new number case inicionumero: receivedNumber = 0; negative = false; break; case '0' ... '9': receivedNumber *= 10; receivedNumber += c - '0'; break; case '-': negative = true; break; } }
void loop() { if(Serial.available()) { processInput(); } valorsensor(); DHT22_ERROR_t errorCode; // El sensor solo puede leer cada 2 segundos y requiere un tiempo de inico de 2 segundos al menos. errorCode = myDHT22.readData(); switch(errorCode) { case DHT_ERROR_NONE: enviodatosT = myDHT22.getTemperatureC()/1.0; dtostrf(enviodatosT,1,2,temperatura); enviodatosH = myDHT22.getHumidity()/1.0; dtostrf(enviodatosH,1,2,humedad); break; } loggerMode(); }
void loggerMode(){ // make a string for assembling the data to log: String dataString = "";
for (int i=0;i<20;i++){
switch (i) { case 1: dataString += String(temperatura); dataString += ";"; break; case 2: dataString += String(temperatura2); dataString += ";"; break; case 3: dataString += String(humedad); dataString += ";"; break;
case 4: dataString += String(humedad2); dataString += ";"; break;
case 5: dataString += String(polvo); dataString += ";"; break; case 6: dataString += String(polvo2); break; } } // open the file. note that only one file can be open at a time, // so you have to close this one before opening another. File dataFile = SD.open("datalog.csv", FILE_WRITE);
// if the file is available, write to it: if (dataFile) { // get Time and Date rtc.GetDate(); rtc.Time(time); rtc.Date(date); // add them to dataString dataString += ";"; dataString += String(date); dataString += ";"; dataString += String(time); // write file if(enviodatosT>0 && temperature>0 && humidity>0 && dust>0) { dataFile.println(dataString);
dataFile.close(); // print to the serial port too: } if(enviodatosT>0 && temperature>0 && humidity>0 && dust>0) { Serial.println(dataString); } } // if the file isn't open, pop up an error: else { Serial.println("error opening datalog.csv"); } }
void valorsensor() { digitalWrite(ledPower,LOW); // power on the LED delayMicroseconds(delayTime); dustVal=analogRead(dustPin); // read the dust value via pin 5 on the sensor delayMicroseconds(delayTime2); digitalWrite(ledPower,HIGH); // turn the LED off delayMicroseconds(offTime); if(dustVal<125) { dustValenvio = 0.0; } else { a=(dustVal*5)/1023; dustValenvio=(a-0.611)/5.1720; } dtostrf(dustValenvio,1,2,polvo); }
So, the receiver code works like this...it stores data to a microSD card in order. I have one module with a temperature/humidity sensor and a dust sensor, the other module (receiver one has them too), so in the datastring the data stores like this: tempvalue1;tempvalue2;humidity1;humidity2;dust1;dust2;date;time tempvalue1 = temperature from sensor in receiver module tempvalue2 = temperature from sensor in sender module humidity1 = humidity from sensor in receiver module humidity2= humidity from sensor in sender module dust1 = dust from sensor in receiver module dust2 = dust from sensor in sender module An image speak for itself...in the image you can see that the sender values don't update with time. Sorry for my bad english, hoping you can help me, regards.
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #9 on: November 18, 2012, 12:33:01 am » |
Aren't you writing thousands of not hundreds of thousands of times a second? Where are you slowing it down to write every few seconds? The temperature won't change in a millisecond will it? Also: Please note that, at present, the String library has bugs as discussed here and here. In particular, the dynamic memory allocation used by the String class may fail and cause random crashes. I recommend reworking your code to manage without String. Use C-style strings instead (strcpy, strcat, strcmp, etc.).
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 9
|
 |
« Reply #10 on: November 18, 2012, 12:42:51 am » |
Yeah, about the slowing time i've already try changing it, from 30 seconds to even 1 or 2, but still the same, i was just trying to see what happen, but ill take a look at those links you wrote and see what can i do, i didn't knew strings could fail.
thanks mr Nick,
regards
Ignacio.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 9
|
 |
« Reply #11 on: November 18, 2012, 03:43:23 am » |
I managed to rebuild the writing code with strcpy and strcat, it works very well but im having the same problem, now i know something else, the switch for the received numbers is functioning way too slow, first it prints the temperature value and then the program keeps running and storing, after 45 sec aprox the switch shows the value of humidity and so... the code for this: #include <SD.h> #include <Wire.h> #include "Rtc.h" #include <DHT22.h> #include <stdio.h> #include <string.h>
char temperatura[10]; char humedad[10]; char polvo[10]; char temperatura2[10]; char humedad2[10]; char polvo2[10]; const char inicionumero = '<'; const char finnumero = '>';
enum { getTemperature, getHumedad, getDust };
int whichNumber = getTemperature;
float temperature, humidity, dust;
float dustValenvio = 0.0; float a = 0.0; int dustPin=0; float dustVal=0.0; int ledPower=2; int delayTime=280; int delayTime2=40; float offTime=9680;
// Cable de datos conectado en pin 4 de Arduino
#define DHT22_PIN 4
// Inicio de Setup DHT22 DHT22 myDHT22(DHT22_PIN);
float enviodatosT = 0; float enviodatosH = 0;
const int chipSelect = 8;
char time[20]; char date[20];
Rtc rtc;
void setup() { Serial.begin(9600); pinMode(ledPower,OUTPUT); pinMode(10, OUTPUT); // see if the card is present and can be initialized: if (!SD.begin(chipSelect)) { // Serial.println("FAILED"); // don't do anything more: return; } Rtc rtc = Rtc(0,0,0,1,1,1,11,0xAA); }
void processNumber (const long n) { float x = n/100.0; switch (whichNumber) { case getTemperature: temperature = x; whichNumber = getHumedad; Serial.print ("Temperature = "); break; case getHumedad: humidity = x; whichNumber = getDust; Serial.print ("Humidity = "); break;
case getDust: dust = x; whichNumber = getTemperature; Serial.print ("Dust = "); break; } Serial.println (x); } void processInput () { static float receivedNumber = 0; static boolean negative = false; byte c = Serial.read (); switch (c) { case finnumero: if (negative) processNumber (- receivedNumber); else processNumber (receivedNumber);
// fall through to start a new number case inicionumero: receivedNumber = 0; negative = false; break; case '0' ... '9': receivedNumber *= 10; receivedNumber += c - '0'; break; case '-': negative = true; break; } } void loop() { if (Serial.available ()) { processInput (); } valorsensor(); DHT22_ERROR_t errorCode; // El sensor solo puede leer cada 2 segundos y requiere un tiempo de inico de 2 segundos al menos. errorCode = myDHT22.readData(); switch(errorCode) { case DHT_ERROR_NONE: enviodatosT = myDHT22.getTemperatureC()/1.0; enviodatosH = myDHT22.getHumidity()/1.0; break; } loggerMode(); delay(5000); }
void loggerMode(){ char datos[100]; rtc.GetDate(); rtc.Time(time); rtc.Date(date); dtostrf(enviodatosT,1,2,temperatura); dtostrf(enviodatosH,1,2,humedad); dtostrf(dustValenvio,1,2,polvo); dtostrf(temperature,1,2,temperatura2); dtostrf(humidity,1,2,humedad2); dtostrf(dust,1,2,polvo2); strcpy (datos, temperatura); strcat (datos, ";"); strcat (datos, temperatura2); strcat (datos, ";"); strcat (datos, humedad); strcat (datos, ";"); strcat (datos, humedad2); strcat (datos, ";"); strcat (datos, polvo); strcat (datos, ";"); strcat (datos, polvo2); strcat (datos, ";"); strcat (datos, date); strcat (datos, ";"); strcat (datos, time); // open the file. note that only one file can be open at a time, // so you have to close this one before opening another. File dataFile = SD.open("datalog.csv", FILE_WRITE);
// if the file is available, write to it: if (dataFile) { // write file if(enviodatosT>0 && temperature > 0) { dataFile.println(datos);
dataFile.close(); // print to the serial port too: } if(enviodatosT>0 && temperature > 0) { Serial.println(datos); } } // if the file isn't open, pop up an error: else { Serial.println("error opening datalog.csv"); } }
void valorsensor() { digitalWrite(ledPower,LOW); // power on the LED delayMicroseconds(delayTime); dustVal=analogRead(dustPin); // read the dust value via pin 5 on the sensor delayMicroseconds(delayTime2); digitalWrite(ledPower,HIGH); // turn the LED off delayMicroseconds(offTime); if(dustVal<125) { dustValenvio = 0.0; } else { a=(dustVal*5)/1023; dustValenvio=(a-0.611)/5.1720; } }
An image of how is it working... Is there a way to fix this?
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #12 on: November 18, 2012, 04:35:38 am » |
You haven't really answered my question about how fast you write the data. Where in the code do you throttle it down so you store figures a second or so apart?
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 9
|
 |
« Reply #13 on: November 18, 2012, 10:48:14 am » |
The data writes when i call the function "loggermode" with a delay of 5 seconds just below it ...
loggermode(); delay(5000); ...
In the image above is stated that the measurings/storing data it's done every 5 seconds. Is this done it right?
|
|
|
|
« Last Edit: November 18, 2012, 11:45:49 am by thrasiel »
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #14 on: November 18, 2012, 02:59:55 pm » |
void loop() { if (Serial.available ()) { processInput (); } ...
loggerMode(); delay(5000);
}
Serial data tends to come in one byte at a time. After each byte you have a 5 second delay. And you are asking why it is slow?
|
|
|
|
|
Logged
|
|
|
|
|
|