How to handle ISR callback? - arduino

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:
Temperature is read an sendTemperatureData(...) is called
cmdMsg3.sendCmdStart(kReadTemperature); is called
cmdMsg3.sendCmdArg(sensorId); is called
ISR from sonar sensor jumps-in
sendTemperatureData(); is called
cmdMsg3.sendCmdStart(kReadSonar); is called
cmdMsg3.sendCmdArg(sensorId);
cmdMsg3.sendCmdArg(distance);
cmdMsg3.sendCmdEnd();
remaining statements from sendTemperatureData(...) are called
cmdMsg3.sendCmdArg(temperature);
cmdMsg3.sendCmdEnd();
resulting in a big mess on the serial communication ...
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?

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.

Related

I2C between EEPROM and Arduino working, not with STM32

This is driving me nuts for a couple of days now, so maybe you guys can give me some insights into what is going wrong.
I'm trying to read some data from an EEPROM (24LC16B) with an STM32(F0), but it just doesn't let me. I've tried an Arduino, which worked and does still work, so I do know that the wiring is correct.
This is my function to read the EEPROM data. (It is cut down to the very basis, just for testing): (Pastebin of my I2C_setup function)
uint16_t readEEPROMData(uint16_t deviceAddress, int memAddress){
// Wait while I2C peripheral is not ready
I2C_WaitForFlag(I2C_ISR_BUSY);
// Start I2C write transfer for 2 bytes, do not end transfer (SoftEnd_Mode)
I2C_TransferHandling(I2C1, 0xA2, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
I2C_WaitForFlag(I2C_ISR_TXIS);
// For testing purpose, be sure to generate a stop command...
I2C_TransferHandling(I2C1, 0xA2, 0, I2C_AutoEnd_Mode, I2C_Generate_Stop);
return I2C_COMM_STATUS;
}
Here's an pastebin of the Arduino library I used.
I've used a logic analyzer to see how the communication is going, and now I really don't understand it. Here's a printscreen of the working Arduino version:
And here's a printscreen of the STM32 communication:
Logic analyzer exports (viewable with Saleae Logic)
As you can see, I'm using the same address (although I had to use 0xA2 with the STM32), and there are no weird things happening, besides the NACK. So what could possible be wrong?
Confirm if all bus timing requirement are satisfied.
Confirm if their is adequate delay after every write cycle (5 mS)
Confirm is bus capacitance falls under permissible limit of I2C (400 pF - Theoretically).
Confirm if the correct VCC is supplied
As mentioned by you are interfacing EEPROM with MCU using cable you need to conform on capacitance.
You can use an oscilloscope to check if their are any distortion in waveform. You can use a LCR meter to check the capacitance.
Try reducing bus speed 25kHz to 50 kHz and check waveform.
Try increasing the strength of pull resister.
The problem with the wrong VCC capacity (4.2v instead of 5v for example) is, that the timing can be different to. (not fully verified, but it fixed the problem)

Sending output from arduino to picaxe

I am doing a class project involving an Arduino Uno and a Picaxe 14m2.
I am in the middle of attempting to code a program for the Arduino Uno that will allow me to send and output value to the input on the Picaxe.
So in layman's, this is what I wish to achieve:
I want the Arduino to check a sensor, and if the sensor returns a specific value. (- I know this part, but not the next.) I then want the Arduino to send a value (HIGH, or 1 .. something like that) as an output to one of the Picaxe input pins. I then need the Picaxe to notice a value has been sent, and then do something else.
Any help would be appreciated.
Thanks.
If you are looking for that, you may want to specify what kind of PICAXE you have.
Since there is a difference in the types of these chips.
After that you may wanna look over the datasheet of the PICAXE so that you can find the instructions set and the type of program memory you have, "EEPROM....".
After that:
List your Is/Os, inputs and outputs.
Set your source code editor.
Write the source code and burn it to the PICAXE program
memory.(C, Assembly...)
Write your Arduino code, setting the Is/Os and telling the
Arduino how to deal with the signals in and out.(C language)
Make a circuit diagram for the hardware you are going to connect
between both chips.
Don't forget to see the loading effects on both the Arduino and
the PICAXE, because you don't want to burn your project hardware
after all.
Test your project and note that you will have to troubleshoot
both software and hardware whenever a problem occurs.
I suggest that you use the Oscilloscope to test the signals going in or coming out of both circuits + the sensor's signal.
For any extra thing you need the PICAXE to do, use If statements, because they are not so technical to implement and they are easy to write and troubleshoot.
For your scheme, you are actually making the Arduino give instructions to the PICAXE through a variable signal coming from a sensor.
^send me feedback and I will help more.
You will probably want to look into using UART (aka Serial) or i2c communication.
Serial communication should work with any PICAXE and Arduino, While i2c Will only work if you are using the X2 Series PICAXE Chips. i2c's main advantage is when using multiple slave devices (plus the master device, i.e. more than just 2 devices total) in which you can use the same two wires for up to around 128 devices. Serial (UART) communication is simpler, and only needs one wire (plus a common ground) to send data one way, it is what i'll show for the rest of this answer
Here is the manual entry for serial input for the PICAXE, and Here's the entry for serial output from the Arduino. The code you will need given your question will be something like the following:
For the arduino:
void setup(){
Serial.begin(9600);
}
void loop(){
if (conditionMet){ //whatever the condition is in your code
int bytesSent = Serial.write(“HIGH”); //send the string “HIGH"
}
}
and for the PICAXE:
main:
serin 6, T9600, ("HIGH") 'uses qualifier to look for exact message "HIGH"
'do whatever when criteria met
goto main

Interrupting with SoftwareSerial on Arduino

I'm using Bluetooth serial port profile to communicate with Arduino. The bluetooth module (HC-06) is connected to my digital pins 10 and 11 (RX, TX). The module is working properly, but I need an interrupt on data receive. I can't periodically check for incoming data as Arduino is working on a time-sensitive task (music-playing through a passive buzzer) and I need control signals to interrupt immediately on receive. I've looked through many documents including Arduino's own site, and they all explain how to establish regular communication using checking for serialPort.available() periodically. I've found one SO question Arduino Serial Interrupts but that's too complicated for my level. Any suggestions on reading realtime input through serial?
Note that the current version of SoftSerial actually uses PCINT to detect the individual bits. Hence I believe defining it again at the main loop would conflict with the SoftSerial's actual detection of bits.
I am reluctant to suggest this as it is modifying a core library. Which is difficult not to do when sharing interrupts. But if desperate, you could modify that routine, with your need.
within
\arduino-1.5.7\hardware\arduino\avr\libraries\SoftwareSerial\SoftwareSerial.cpp.
//
// The receive routine called by the interrupt handler
//
void SoftwareSerial::recv()
{
...
// if buffer full, set the overflow flag and return
if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF != _receive_buffer_head)
{
// save new data in buffer: tail points to where byte goes
_receive_buffer[_receive_buffer_tail] = d; // save new byte
_receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF;
#ifdef YOUR_THING_ENABLE
// Quickly check if it is what you want and DO YOUR THING HERE!
#endif
}
...
}
But beware your are still in a ISR and all Interrupts are OFF and you are blocking EVERYTHING. One should not lollygag nor dilly dally, here. Do you something quick and get out.

Re-entrant code?

I am using the Wire library to move some data from a shield to the Arduino.
The shield always puts out exactly 36 bytes (GPS device), but the Arduino sees two data transfers that together add up to 36 bytes. Not always the same count in each of the transfers, but always 36 bytes total. The setup() routine is re-run every time the data comes in.
It's as if the library is re-entering the Arduino application and screwing it royally.
Is this possible?
If setUp() is being run every time data comes in (and are you sure about this?) then this means that the Arduino is resetting/restarting for some reason. How are you powering the GPS shield? If it is glitching the power to the Arduino (drawing too much current perhaps) then that could cause a reset. It could also be something with your code using up too much memory. I'd look at the power issue first.
If you are using Wire.available, note that it does not guarantee that it will return the number of bytes sent. You will need to call it repeatedly until it returns zero.
See an example in the Arduino documentation.

Arduino interrupt handling

Background
I have need for a data logging application running on a "Arduino compatible" chipKit UNO32 board, with a connected sensor. Data should be logged to an SD-card found on a "Arduino Wireless SD shield".
The sensor is connected via I2C.
My problem is that when I use the Arduino SD library writing is slow: 25 ms per print() operation, which gives me a maximum of 40 Hz which is laughable compared to the 100-800Hz data rate of my sensor.
My faulty solution
Luckily the sensor comes equipped with both an on-chip FIFO that can store 32 sensor values. This means I can go to at least 200Hz without any troubles since the time to fill the FIFO is way larger than the time to write to the card.
But I'd still really like to get to at least 400Hz, so I thought I'd have the following setup:
Tell sensor to put data in the FIFO
When the FIFO is almost full, the sensor triggers an interrupt (sensor does this, and it works, I can catch the interrupt)
When the Arduino receives the interrupt, it polls the sensor for data (via I2C) and stores the data in a buffer in SRAM.
When the SRAM buffer is getting full, write its contents to the SD-card.
Unfortunately, this does not seem to work since the Arduino Wire library that handles I2C use interrupts, and can not be called from within an interrupt handler. I have tried it, and it freezes the microcontroller.
My question
There seems to be other I2C libraries for Arduino that do not rely on interrupts. Should I try that route?
Or is my way of thinking (grabbing a load of data in an ISR) bad from start? Is there another approach I should take?
Just use the interrupt to set a flag and finish the ISR. from the main loop do the calling I2C instead of directly calling from ISR.
boolean fifoFull = false;
void fifoISR() {
fifoFull = true;
}
void loop() {
if(fifoFull) {
// Do I2C
fifoFull = false;
}
}

Resources