Reputation: 3823
I am currently working on a larger Arduino Mega 2560 project; servo controlling and sensor readings involved. I am using ultrasonic proximity sensors (HC-SR04) with the NewPing 1.8 library that uses interrupts for detecting the echo of the sensors. Furthermore, I read temperature and light intensity measurements also. Distance data received from the HC-SR04 ultrasonic sensors are forwarded over USB to the host computer by using the cmdMessenger3 library. Servos are controlled by messages from the host computer using the standard Servo library.
The mess begins as soon as the NewPing library calls the ISR
when the ultrasonic echo is detected. This is the function called by the NewPing library when distance data is available:
void sendSonarData(uint8_t sensorId, uint16_t distance) {
//noInterrupts();
cmdMsg3.sendCmdStart(kReadSonar);
cmdMsg3.sendCmdArg(sensorId);
cmdMsg3.sendCmdArg(distance);
cmdMsg3.sendCmdEnd();
//interrupts();
}
Here's another callback that sends temperature data to the host computer by the cmdMessenger3 library:
void sendTemperatureData(uint8_t sensorId, uint16_t temperature) {
//noInterrupts();
cmdMsg3.sendCmdStart(kReadTemperature);
cmdMsg3.sendCmdArg(sensorId);
cmdMsg3.sendCmdArg(temperature);
cmdMsg3.sendCmdEnd();
//interrupts();
}
Problem: While the Arduino e.g. tries to send the temperature data, the ISR from the ultrasonic sensor might jump-in and writes it's own data to the serial stream; ending up in a mess regarding the serial data sent to the host computer, because the sending process is not atomic, consisting of multiple commands to send one message (sendCmdStart
->sendCmdArg
->sendCmdEnd
).
Here's an example:
sendTemperatureData(...)
is calledcmdMsg3.sendCmdStart(kReadTemperature);
is calledcmdMsg3.sendCmdArg(sensorId);
is calledsendTemperatureData();
is calledcmdMsg3.sendCmdStart(kReadSonar);
is calledcmdMsg3.sendCmdArg(sensorId);
cmdMsg3.sendCmdArg(distance);
cmdMsg3.sendCmdEnd();
sendTemperatureData(...)
are calledcmdMsg3.sendCmdArg(temperature);
cmdMsg3.sendCmdEnd();
I then tried to prevent ISR calls during the send process by using noInterrupts()/interrupts()
; making the send process kind of 'atomic'. But this leads to lots of other problems, millis()/micros()
functions are not precise anymore, Serial communication breaks down, etc.; all relying on the timers that are disabled by noInterrupts()
. Moreover, the servos behave also quite strange, since timing for the PWM signal generation seems to be messed up also.
Any ideas how to solve this 'concurrency' issue without breaking the interrupt-based paradigm in my program?
Upvotes: 0
Views: 675
Reputation: 9587
You are trying to do WAY to much work in your ISRs; generally speaking, they shouldn't do anything that requires a delay (and any serial message over 1 byte falls in that category). A more reasonable implementation would have the ISR simply store the sensor reading in global variables, and set a flag that the main program checks to see if it should send the serial message. You might need something fancier (like a message queue) if a second reading from the same sensor might arrive before the previous one had a chance to be sent.
Upvotes: 5