I'm sure this has been asked numerous times so sorry about that, but I really didn't find anything that worked from google. Also, this is my first post, so hello!
I have two arduino unos: one is supposed to be keeping track of some weather meters I have hooked up to it, and the other one is supposed to "poll" the sensor values from it and send them to internet. You might ask why I want to have two arduinos? Well, the little features I wanted didn't quite fit in one.
So far I have successfully managed to create a really low-level communication protocol, which consists of: send a sensor number (0-9) and receive the value followed with "§". I have spent the last few hours looking for solution on how to properly turn the received string to float value.
The code I have on the sensor-arduino's loop:
if (Serial.available()) {
int humidity;
float temperature;
int sensstatus = DHT.read22(A0); //Get the temperature and humidity from sensor
temperature = DHT.temperature;
humidity = DHT.humidity;
int inByte = Serial.read();
if (inByte == 167) { //If the received byte equals "§", send "§" back. Kinda like "ping".
Serial.write(167);
goto revert; //Goes back to loop
}
switch (inByte) {
case '5':
Serial.print(rainmm);
Serial.write('§');
break;
case '6':
Serial.print(avgwindspeed);
Serial.write('§');
break;
case '7':
Serial.print(avgwindspeed2);
Serial.write('§');
break;
case '2':
Serial.print(humidity);
Serial.write('§');
break;
case '1':
Serial.print(temperature);
Serial.write('§');
break;
}
}
And the function receiving the values on the other arduino:
float query(byte x) { //<- byte x is the number of the sensor I want to poll
while(sSerial.available()) sSerial.read(); //the serial is called sSerial because I use software serial.
sSerial.print(x); //Send the sensor number.
String input; //Create variable for the incoming data
byte inbyte;
while(inbyte != 167) { //Go to loop until the received char equals "§".
if (sSerial.available()) {
inbyte = sSerial.read();
if (inbyte != 167) {
char inchar = inbyte; //Turn the byte to char.
input += inchar; //Add the char to the String.
}
}
}
char outchar[6]; //Create char array out of the string
input.toCharArray(outchar, 6);
Serial.println(input); //Just for debug
return(atoi(outchar)); //Turn the value to float. Which doesn't work.
}
The "return(atoi(outchar));" doesn't return decimals at all. And that is the problem.
The result of using that function is:
2.54 //This one is caused by the "Serial.println(input)" thingy I used for debugging.
2.00 //And this is the value returned.
I have tried to split the variable to two, and added the splitted variables to the float. First the whole numbers and then the decimals. But that wasn't really very cost-effective and I also ran in to problems when the length of the variables changed.
So my question is, is there any magical way for converting I could use for the float over serial transmission?
I have all the basic weather station features + wind turbine simulator + sd card datalogging + pachube (cosm?) broadcast + 3g modem + terminal-like interface for changing settings and such. I had all that (excluding the terminal and wind turbine simulator) in one arduino, but I thought it would be easier to split the workload for two. Also, I thought it would be easier to have one of the arduinos at the top of the pole housing the meters and one closer the ground, which I could interface with.
The "return(atoi(outchar));" doesn't return decimals at all. And that is the problem.
That's because "atoi" means "ASCII to Integer". Integer's don't have a decimal component.
Personally I would decide upon a required precision, say 3 decimal places, then multiply the value up before sending it - then divide it upon reception.
For example, to send the value 6.4827643:
// Sending end:
float value = 6.4827643;
int sendvalue;
sendvalue = value * 1000; // sendvalue now contains 6482
// At the receiving end:
int receivedvaue = atoi(valueFromSerial);
float value = sendvalue / 1000.0; // value now contains 6.482
I just found a quick solution for the problem!
I replaced "return(atoi(outchar))" with "return(atof(outchar))"!
It seems to output a float with two decimals.
Then at the receiving end you look for character 1 (Start of Header) followed by character 2 (Start of Text), then read sizeof(struct transfer) bytes into a struct transfer variable's address, and confirm it's right by expecting a character 3 (End of Text) followed by a character 4 (End of Transmission).
Those characters are standard characters reserved for just that purpose. You could transfer some packet identifying data (like a packet number, etc) between the SOH and SOT characters if you like...
majenko:
then read sizeof(struct transfer) bytes into a struct transfer variable's address
What do you exactly mean by that? I understand it like: "wait for this many (sizeof(struct transfer)) bytes and insert them in the struct", am I correct? I googled around but didn't find a way to input the data in the struct.
So far I have managed to receive the first two characters like this:
#include <SoftwareSerial.h>
SoftwareSerial sSerial(7, 8);
void setup() {
Serial.begin(9600);
sSerial.begin(9600);
}
void loop() {
if(sSerial.available()) {
if (sSerial.read() == '\001') {
Serial.println("FIRST");
while(!sSerial.available());
if (sSerial.read() == '\002') {
Serial.println("SECOND");
while(!sSerial.available());
byte inByte;
while(inByte != '\003') {
inByte = sSerial.read();
//And I thought the magic should happen here.
}
Serial.println("DONE");
}
}
}
}
I basically copy-pasted the code you provided to the other arduino, and made it send the values every 10 seconds.
You treat the structure variable as if it were a string of characters. The & operator returns the address of the variable in memory.
You can use "(char *)&myStructVariable" and it will be the same as using any char * variable.
The sizeof() returns the number of bytes the variable is made up from, so no matter how you change the structure it will always expect the right number of bytes.