I am working on an embedded system whose microcontroller is an Arduino chip (Arduino Nano ATmega328P).
I can connect to it with a FTDI cable to read its outputs using a serial terminal. Now I want to communicate with it the other way around, that is, sending it messages or codes in order to change its global parameters.
I know it is possible to do so if I write some code where the Arduino is doing nothing but listening to the serial port. But I would like to be able to send it message at any time, even when it is doing something else. Sending it a message could trigger an interrupt, then the Arduino would execute a parallel script where it listens to the serial port for some time...
Can I do that, or is it not possible with an Arduino ?
Thanks :)
As far as I know (could be wrong), you can not "multi-thread" an arduino (don't know of any microcontrollers that support this). Inherently microcontrollers should be coded in a different style from computers. You can not assume infinite resources and infinite threads. Instead it is all about your main loop speed and how to distribute your resources. The arduino example (blink without delay) is an example of how this is done with timers. Basically, you put all of your processes inside their own timers so that they execute once per timer interval and are skipped at all other times. Assuming none of your processes are in the main loop, this will allow context switching after each and every process, as the program will cycle back through the main loop looking for the next task timer to come up. If you put all of your cyclical processes on timers like this, then put your serial availability check in another (pretty high frequency) timer, and grab serial any time you see it, it will I believe give you the results that you are after. Keep in mind though that serial read and write is a relatively slow process and so after reading or writing you may need to be careful about other processes that depend on a tight frequency call (like reading a mic to determine sound frequency for instance would need to be reset every time you read serial).
--edit
I was literally working on this today anyways, so here you go:
*note that i had no need to read incoming serial, so left that bit to you to figure out. (but showed where it would be done)
boolean resetMicRecording = true;
unsigned long microphoneTimer = 0;
unsigned long microphonePeriod = 100; //us
unsigned long serialTimer = 0;
unsigned long serialPeriod = 500; //ms
void setup() {
Serial.begin(9600);
}
void loop() {
if (micros() - microphoneTimer >= microphonePeriod)
{
microphoneTimer = micros();
//do microphone task
if(resetMicRecording)//first time flag
{
//set inital recording stuff
resetMicRecording = false;
}
else//consistent frequency sampling
{
//precision analog reading, logging, and processing if nec
}
}
if (millis() - serialTimer >= serialPeriod)
{
serialTimer = millis();
if(Serial.available()>0)
{
//read your incoming serial here
//reset sensitive device timers to startup
resetMicRecording = true;
}
}
}
Related
I have steppers stepping during an interrupt timer at 50 and have all my code working between the interrupts until I tried reading serial commands more than one character long.
I'm getting dropped bytes so my strings are missing a letter every 4-5 chars. I researched all day to try and figure out a solution but have come up with nothing. If I don't use an interrupt my stepper stops for 2 seconds reading a one char serial input as a string.
My goal is to have a remote control app sending speed commands. I need help working this problem out.
https://sourceforge.net/p/open-slider/code/ci/master/tree/OpenSliderFirmware/
String incomingString = "";
if (Serial.available() > 0) {
incomingString = Serial.readString();
Serial.println(incomingString);
}
Using Accelstepper library
interrupt:
//Interrupt Timer1
void ISR_stepperManager() {
Slide.runSpeed();
Xaxis.runSpeed();
Yaxis.runSpeed();
}
Quick answer: you don't if the interrupt timer is cutting in too often.
I resolved the problem by using a variable interrupt timer and a step multiplier. Basically the steps are called every time the timer interrupts instead of checking millis inside the interrupt function. This solved many issues. The speed of the stepper is now controlled by the interrupt timer. This gave me more free cycles to fully read the incoming serial without corruption and improved efficiency. Calling more steps per cycle when doing over 4k steps/s also improved efficiency requiring less cycles for a high rate of steps.
The serial is processed one char per cycle to prevent blocking.
Overall, if you are using serial and an interrupt timer, any interrupt happening < 100us you should be cautious how much code you are running during the interrupt. It will cause issues with incoming serial and user inputs. A few lines of code in a 25us timer interrupt, incoming serial will not function.
i'm not sure if it will help to your problem, but i saw along the time that the String type is not safe to use when other things need to happened.
i prefer to use char array and read one char at a time.
while(Serial.available())
{
data[x] = Serial.read();
x++;
}
i'm finding it much more reliable.
hope it's help!
The function of the program I am writing is to stream incoming analog data from a sensor to a program on my computer across the USB port. For a little fun, I've decided to add a button to the program that will turn on/off a lamp. The lamp will be connected to a relay, which is connected to the arduino. I know how to go about programming it, but I want to know if this will interrupt the sensor data transfer?
I will get the current state of the light (HIGH(1) or LOW(0)) from the arduino when the button is pressed, then write to the arduino (HIGH(1) or LOW(0)) depending on the current state. I have a 5 second delay between each loop of the arduino program, for reasons related to the sensor output; however, I think I'm going to have to change this so that when the button is pressed, it is not missed by the arduino loop, or is that not possible?
I thought I read somewhere that you can't transmit/receive streaming data on the same serial line...in which case, I will need the Mega.
You have to remember and think of the Arduino as a single threaded device. While it is doing anything it is not able to do anything else. Period! In regard to the serial port however, the buffer will still accept incoming data on RX, however if an overflow situation occurs whilst blocked, management is impossible.
See the following taken directly from the Arduino reference
Certain things do go on while the delay() function is controlling the Atmega chip however, because the delay function does not disable interrupts. Serial communication that appears at the RX pin is recorded, PWM (analogWrite) values and pin states are maintained, and interrupts will work as they should. Reference
Now in saying that when you are setting the delay to 5 seconds between loops ( delay(5000) ) you are essentially blocking it from doing anything else almost full stop.
The Arduino framework exposes a a counter ( millis() ) that basically runs from the moment of boot for roughly 50 days in increments of one (1) millisecond. See Arduino - millis()
In your application you would define (remember) what loop you were due to run & when the said loop had finished so to not allow the other loop to run until the millis() counter was a defined amount more than your count. (Remember to define the count as a long)
Then what you do is move your loops out into separate functions that will only execute if the if statement return true...
for example...
long interval = 5000; // Define interval outside of the main loop
long previousCount = 0; // Used to store milli count when finished
int loopPosition = 1;
void loop()
{
if ((long)millis() - previousCount >= 5000 )
// This if statement will only return true every 5 seconds (5000ms)
{
if (loopPosition == 1)
{
function_One();
previousCount = millis(); // Redefine previousCount to now
loopPosition++; // Increment loop position
}
else if (loopPosition == 2)
{
function_Two();
previousCount = millis();
loopPosition--; // Decrement loop position
}
}
// Do Anything Here You Want
// - While ever the if statement above returns false
// the loop will move to this without hesitation so
// you can do things like monitor a pin low / high scenario.
}
void function_One()
{
// Do Your First Loop
}
void function_Two()
{
// Do Your Second Loop
}
The above will stop any delay you are using from blocking awesomeness, and to more of a point, almost make delay obsolete if implemented correctly under the right scenarios.
In regard to your serial data comment, like i said at the top of this article, the Arduino can only do one thing at a time. Transmitting and receiving at exactly the same time is impossible even with the Mega. In saying that a board like the 'Uno' for example is only capable of one serial interface, where as the 'Mega' is capable of four.
Best of luck....
NB- For a beginner reading this, the following tutorial / example covers what i have above in fairly simple terms and is a great building block for further awesomeness! Arduino - Blink Without Delay
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.
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;
}
}
i am having trouble getting data from two sensors using two software serial ports with an arduino board. I noticed a similar question might have been asked before but the answers suggest it can't be done and I know fully well it can based on the example here (http://arduino.cc/en/Tutorial/TwoPortReceive)!
I am using an arduino ethernet. The devices I am trying to get data from include a GPS and an IMU both from sparkfun.
I can get data from either devices using just on software serial port but as soon as I add the second software serial port, neither ports will work. I can't use the hardware serial port because that is being used byt another device.
My code is exactly similar to the example:
#include <SoftwareSerial.h>
SoftwareSerial portOne(7,8);
SoftwareSerial portTwo(5,6);
void setup()
{
Serial.begin(9600);
portOne.begin(9600);
portTwo.begin(9600);
}
void loop()
{
portOne.listen();
while (portOne.available() > 0) {
char inByte = portOne.read();
Serial.write(inByte);
}
delay(500);
portTwo.listen();
while (portTwo.available() > 0) {
char inByte = portTwo.read();
Serial.write(inByte);
}
Serial.println();
}
Anyone with any ideas?
This code will not work, or will work poorly if it works at all. SoftwareSerial only has one internal buffer. Yes, you can have multiple SoftwareSerial objects in existence, but only one of them controls the internal buffer. When any RX pin gets asserted, that generates an interrupt, but only the listen()ing RX pin gets checked for a start bit.
What's really needed is the ability to check on multiple pins when an interrupt comes along from the start bit. Then you'd have to set up pointers to the appropriate data structures. It would be complicated, but possible.
Or maybe just give up on interrupt-driven reception, and spin on checking both/all of the RX pins, and start the receive based on the pin you see. Be forwarned that this code has much hair, and you WILL need an oscilloscope to make it work.
I'm having a similar problem, which is why I found your sensor. After talking it over with my co-workers, we've decided to read our sensors in rotating order. Our sensors report the current state of the sensor, and not specific events, so it's okay if we lose some reports. So we'll read from port 1, then read from port 2, then port 1, etc. Our sensors spit out lines of text, so we know when to switch to the next sensor.
The referenced example only actively listens to one port at a time. The recommended solution would be to upgrade to an Arduino Mega (https://www.sparkfun.com/products/11061) which has 4 hardware serial ports.
In order to simultaneously support two software serial ports is going to require a lot of the CPU resources. It also be a difficult design and excessive programming time far outweighing the cost of $58 + shipping.
Looking at you code again it occurs to me that you are immediately checking for characters after your portOne.listen command. At 9600 baud it will take approximately 1ms for the first character to arrive, your while test will have been completed and the portTwo.listen command executed long before the first character arrives.
For testing purposes try adding a 1-2 ms delay after the portOne.listen command and see if you get a character.
As an example (untested and note, if port one is sending characters with no intercharacter gaps, the first while will never fail, preventing reading portTwo characters):
void loop()
{
portOne.listen();
delay(2);
while (portOne.available() > 0) {
char inByte = portOne.read();
Serial.write(inByte);
delay(1);
}
portTwo.listen();
delay(2);
while (portTwo.available() > 0) {
char inByte = portTwo.read();
Serial.write(inByte);
delay(1);
}
Serial.println();
}
Don't use while ......
Use:
{ portOne.listen();
if (PortOne.available() ) {
ricevo = myPort1.read(); }
// delay(2); // ridiculos waiting time
// delay(1); // extra ridiculos waiting time
Than 500 ms is a too big time for switching, no time.....