I used this simple code to understand the basics of serial communication. This achieves simple communication between the computer and arduino:
String receive_buffer;
void setup() {
Serial.begin(9600);
}
void loop() {
if(Serial.available()>0) //check for any data received
{
receive_buffer = Serial.readString(); //read received data
Serial.print("received data is: ");
Serial.println(receive_buffer); //display received data
}
}
This works. Now, my next idea was to try communicating between two arduinos. I used this code for the reciever arduino.
The sender arduino contains a motor fitted with an encoder, and using the ppr value I calculate the rpm of the motor. The code for rpm works perfectly. I now want to communicate this data to the receiver.
Wiring: (RX,TX) of sender connected to (TX,RX) of receiver, and GND
is made common.
Now, on arduino.cc, it says that Serial.write() can have String as an input. So, I tried typecasting the rpm value (which was int), into a String, and then send that string to the receiver through Serial.write().
float ppr=512.0;
int encpin=3;
volatile long pulsecount=0;
float revs;
float rps;
int rpm;
extern volatile unsigned long timer0_millis;
void setup()
{
pinMode(encpin,INPUT_PULLUP);
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(encpin),function,RISING);
//when pulse is detected
//interrupt triggered,
//function callled
}
//increments pulse by one
void function(){
pulsecount+=1;
}
void loop()
{
revs= pulsecount/ppr ;
if(millis()>=1000)
{
rps= revs;
rpm=rps*60;
pulsecount=0; //resets pulsecount
String strrpm= (String)rpm;
//Serial.println(rpm);
Serial.write(strrpm);
// Serial.println("string rpm");
//Serial.println(strrpm);
noInterrupts (); //resests millis() to 0
timer0_millis = 0;
interrupts ();
}
}
However, I get an error:
43:24: error: no matching function for call to 'HardwareSerial::write(String&)'
The issue is not with typecasting. I tried to simply print strrpm and it worked fine. When tried Serial.write() with a different and simple string, i.e Serial.write("abcdef");
, it still gave the same error.
How then should I should I send my strrpm (string rpm) value to the receiver?
1 Answer 1
Serial.write()
is meant for sending binary data. So if you want to send a string with it, you need to provide a c-string, not the Arduino implementation of the String
class. That's done easily with a member function of that class:
Serial.write(strrpm.c_str());
That compiles successfully for me.
Though you should not do it like this. The String
class is generally bad on small microcontrollers, especially on the AVR microcontrollers with little memory (like Uno, Nano, Mega, ...), because it causes memory fragmentation (see Majenkos "The Evils of Arduino Strings").
Instead you should just use the corresponding print()
functions of the Serial
class. They are made for sending human readable strings. No need to first build up a String
object. Just do
Serial.println(rpm);
And it will do the string conversion itself without using the String
class.
Besides that I have some notes on your code:
The way you use
millis()
is a bit weird. There is no need to actually reset themillis()
value itself. Normally you would hold a timestamp in a variable and then only calculate the difference betweenmillis()
and the timestamp. That is also overflow safe. Like thisunsigned long timestamp = 0; ... if(millis()-timestamp >= 1000){ ... timestamp+=1000; }
That doesn't use an extern variable, it is not resetting the
millis()
counter (which might mess with other things too, that rely on it) and is somewhat the standard way to do it.You are using an atomic section for resetting the
millis()
counter, but not for accessingpulsecount
. That is actually the place, where you absolutely need this.pulsecount
is of typelong
, so it will need many instructions to do the calculation forrevs
. The your interrupt service routine can easily mess with the data in that moment, resulting in garbled data. Move the calculation into the if statement and use an atomic section like with themillis()
counter to read it into a local variable then you can do the calculations. That will keep the time, where interrupts are turned off very short. You can also - instead of turning all interrupts off - just detach the pin interrupt for that time, so that other things are not affected. Like this code:void loop(){ if(millis()-timestamp > 1000){ // deactivate our pin interrupt, so it cannot mess with the data detachInterrupt(digitalPinToInterrupt(encpin)); // copy pulsecount to local variable for further calculation long local_pulsecount = pulsecount; // reactivate our pin interrupt attachInterrupt(digitalPinToInterrupt(encpin),function,RISING); // Do calculation and print the value ... timestamp += 1000; } }
-
" Instead you should just use the corresponding print() functions of the Serial class. They are made for sending human readable strings. No need to first build up a String object. Just do Serial.println(rpm)" But I dont see how this makes the arduinos communicate?satan 29– satan 292021年05月04日 09:02:51 +00:00Commented May 4, 2021 at 9:02
-
Also, your initial suggestion: the code ccompiles succesfully, and prints the values on the serial monitor of the sender: but the serial monitor of the receiver is blanksatan 29– satan 292021年05月04日 09:08:59 +00:00Commented May 4, 2021 at 9:08
-
Serial.println()
will already send the data in human readable form via serial. Thus it is already communication. Please try usingSerial.readStringUntil('\n')
instead ofSerial.readString()
. The latter has a 1s timeout. Since you are sending every second there is less than 1s inbetween each transmission. This might cause that function to never exit. The former function call will read only until the newline character, which is automativally added by theprintln()
functionchrisl– chrisl2021年05月04日 21:32:13 +00:00Commented May 4, 2021 at 21:32