Arduino reading input in a loop with in a loop - arduino

I have a sensor connected to the arduino uno pin 5 and it reads the sensor value every second and send out a sms every 1 minute. This works.
void loop() {
// read_sensor_and_store(5);
// increment_time_counter();
// if_time_counter is 60000 miliseconds then send the sms
delay(1000);
}
While this works, I want to call another function, say, read_another_sensor(pin, delay_0, count). What this will do is read the particular pin 'count' number of times with delay of 'delay_0'. (baically it will run a for loop with the given delay).
Now if I have something like this
void loop() {
// read_sensor_and_store(5);
// read_another_sensor(7, 2000, 4);
// increment_time_counter();
// if_time_counter is 60000 miliseconds then send the sms
delay(1000);
}
This too will work but while executing the read_another_sensor() the time will elapse and I will miss few readings of pin 5. Is there a way to execute these two functions in parallel or any other way to achieve the objective of "calling of read_another_sensor will not affect the continuous periodic functionality of read_sensor_and_store"
Appreciate any insight on this matter.
Thank you

Extending the answer of the user above, you can do something like this to allow you to spawn sensor2 task with arbitrary (multiply of 50 mili) periods:
const int ONE_SECOND = 1000;
const int ONE_MINUTE = 60*ONE_SECOND;
// 50 mili - has to be upper bound of total execution time
// has to be [GCD][1] of periods of all your tasks
const int BASIC_PERIOD = 50;
// FRAMES*BASIC_PERIOD has to be [LCM][2] of periods of all your tasks
const int FRAMES = ONE_MINUTE / BASIC_PERIOD;
void read_another_sensor(delay_0, count) {
g_delay = delay_0;
g_count = count;
}
...
void loop() {
if ((unsigned long)(millis() - previousMillis) >= BASIC_PERIOD) {
previousMillis = millis();
frame_counter = (frame_counter + 1) % FRAMES;
if (frame_counter == 0)
send_sms();
if frame_counter % ((FRAMES * BASIC_PERIOD) / ONE_SECOND) == 0
read_sensor();
if frame_counter % ((FRAMES * BASIC_PERIOD) / g_delay) == 0
if (count > 0) {
count--;
read_sensor_2():
}
}
}
}
If you need to use more periodic tasks with periods that are not multiply of 50 mili, your system becomes complex wor have high rate of deadline misses, please read about "cyclic executives".
GCD
LCM

In general you can replace delay(1000) with a timestamp comparing code to reach your goal. The code construction (see below) is often used if one function needs to executed in an different interval than others or if you want to combine sketches. There are already several sources on the web and stackoverflow (e.g. see arduino-combine-sketches).
unsigned long interval=1000; // the time we need to wait
unsigned long previousMillis=0; // millis() returns an unsigned long.
void setup() {
//...
}
void loop() {
if ((unsigned long)(millis() - previousMillis) >= interval) {
previousMillis = millis();
// ...
}
}
//...
So in your case you need something like the following code, that I have not tested, but hopefully gives you an idea how you can manage the functions without delay():
unsigned long interval=1000; // the time we need to wait (read_another_sensor)
unsigned long previousMillis=0; // millis() returns an unsigned long.
void setup() {
//...
}
void loop() {
// every 1000 millisecs (=delay(1000))...
if ((unsigned long)(millis() - previousMillis) >= interval) {
previousMillis = millis();
read_another_sensor();
//...
}
read_sensor_and_store() // continuous periodic
// ...
}

Related

I'm using interrupts to detect button press lengths, but it will sometimes not detect that I've released the button

#include <DS3231.h>
DS3231 clock;
RTCDateTime dt;
const int INTERRUPT_PIN = 2;
int prevTime, pressedTime, releasedTime, heldTime;
bool settingTimes = false;
int startHour1, startMin1, startHour2, startMin2, endHour1, endMin1, endHour2 = 0;
void setup() {
Serial.begin(9600);
// Initialize DS3231
clock.begin();
clock.setDateTime(__DATE__, __TIME__);
pinMode(INTERRUPT_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), actButtonPress, CHANGE);
}
void loop() {
if (!settingTimes) {
dt = clock.getDateTime();
// For leading zero look to DS3231_dateformat example
Serial.print(dt.hour); Serial.print(":");
Serial.println(dt.minute);
delay(1000);
} else {
//Serial.println("Setting Times, Check the buttons");
}
}
void actButtonPress() {
if (prevTime + 100 < millis()) {
Serial.println("This is being called");
if (digitalRead(INTERRUPT_PIN) == LOW) { //Button has been pressed
pressedTime = millis();
} else {
releasedTime = millis();
heldTime = releasedTime - pressedTime;
if (heldTime > 3000) {
settingTimes = !settingTimes;
} else {
Serial.println("Change Page");
}
}
prevTime = millis();
}
}
I want to be able to press the button for 3 seconds to set times, and anything less times, I want to change pages. I need to use interrupts because the ds3231 get date time function doesn't seem to work without the delay statement (I've already tried the millis() function)
Using interrupts is generally not a good idea for checking button presses. Switch inputs should always be considered as rather dirty signals... The initial contact is never clean-cut, there is always some noise before the signal is clean. That's called "bounce".
To debounce your switch input you should either:
wait a little while using a counter, or a time stamp, (using millis) before checking if the button is released.
or only poll the switches at regular intervals.
The duration of the bounce depends on the switch, and will degrade over time as the switch gets older. In practice, 30 ms is a good value for the delay.
I suggest you try this:
#include <DS3231.h>
DS3231 clock;
RTCDateTime dt;
// putting all switch states in this struct to take advantage of bit-sized
// storage, and save RAM space for the rest of the program.
struct SwitchStates
{
unsigned char action : 1; // state of action switch.
unsigned char sw1 : 1; // add one bit for each switch you have.
};
// you need to keep track of previous switch states to detect changes.
SwitchStates previousSwitchState;
#define ACTION_SWITCH_PIN (2)
#define SWITCH_TIME_SET_DELAY (3000) // time to hold switch for setting time in ms.
#define SWITCH_POLLING_DELAY (30) // switch polling delay, in ms.
#define RTC_READ_DELAY (1000) // RTC read delay in ms.
void setup()
{
Serial.begin(9600);
// Initialize DS3231
clock.begin();
clock.setDateTime(__DATE__, __TIME__); // imho, that's not a very good idea...
pinMode(ACTION_SWITCH_PIN, INPUT_PULLUP);
digitalWrite(ACTION_SWITCH_PIN, HIGH); // you must set the pin high
// for pull-up to work.
}
// only used witin loop()
static unsigned char switchPollingTimeStamp = 0; // char because 30 is less than 256.
static bool settingTimes = false;
static unsigned int rtcReadTimeStamp = 0;
static unsigned int modeSwitchTimeStamp = 0;
void loop()
{
// get current timestamp.
unsigned long now = millis();
if ((unsigned char)now - switchPollingTimeStamp >= SWITCH_POLLING_DELAY)
{
switchPollingTimeStamp = (unsigned char)now;
// all switch inputs will be handled within this block.
SwitchStates curSwitchState;
// polling at regular intervals, first read your inputs.
curSwitchState.action = digitalRead(ACTION_SWITCH_PIN);
// curSwitchState.sw1 = digitalRead(SW1_PIN);
// etc...
// check activity on action (mode) switch and do timing, etc...
if (curSwitchState.action && !previousSwitchState.action)
{
// keep track of how long the switch has been held
modeSwitchTimeStamp = (unsigned int)now;
if (settingTimes)
settingTimes = false; // exit time set mode, for example.
else
Serial.pritln("Next Page"); //
}
else if (curSwitchState.action)
{
// after 3000 ms, switch to timeset mode.
if ((unsigned int)now - modeSwitchTimeStamp >= SWITCH_TIME_SET_DELAY)
{
if (!settingTimes)
{
settingTimes = true:
Serial.println("Entering time set mode");
}
}
}
// That was the most complex one, others should be easier
if (settingTimes)
{
if (curState.sw1 && !previousSwitchState.sw1)
{
// whatever this switch would do in set time state.
}
// etc...
}
else
{
// the same when not in set time state.
}
// to keep track of switch activity.
// by first storing current switch states, then saving them
// like this, you cannot forget one, this avoids any 'I missed one' bugs.
previousSwitchState = curSwitchState;
}
// try to keep your code non blocking, so your device stays responsive...
// in other words, avoid calling delay().
if (!settingTimes && (unsigned int)now - rtcReadTimeStamp >= RTC_READ_DELAY)
{
rtcReadTimeStamp - (unsigned int)millis();
dt = clock.getDateTime();
// For leading zero look to DS3231_dateformat example
Serial.print(dt.hour); Serial.print(":");
Serial.println(dt.minute);
}
}
Note: I don't have an arduino with me, and the laptop I have is not set up for it, so I coud not compile and debug it. It should be very close, though.

Arduino vl53l0x sensor

I'm trying to make a toilet sensorish trigger using vl53l0x sensor, I'm having trouble firing an action while my hand is in front of the sensor for 5 seconds or so, while I've tried different versions of blinkwithoutdelay sketches, and other methods found online, all of them, trigger the 5 seconds, after I pulled my hand off the sensor, which is not what I want. Thanks in advance, I posted my sketch to what I got so far. Thanks in advance!
// Library for TOF SENSOR
#include <Wire.h>
#include <VL53L0X.h>
VL53L0X sensor;
// Time calculation
unsigned long startTime;
unsigned long endTime; // store end time here
unsigned long duration; // duration stored
byte timerRunning;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Wire.begin();
sensor.init();
sensor.setTimeout(500);
// Start continuous back-to-back mode (take readings as
// fast as possible). To use continuous timed mode
// instead, provide a desired inter-measurement period in
// ms (e.g. sensor.startContinuous(100)).
sensor.startContinuous();
}
void loop() {
// put your main code here, to run repeatedly:
delay(1000);
int tofdata = sensor.readRangeContinuousMillimeters();
int distance = tofdata / 10; // convert mm to cm
Serial.print( distance ); // print new converted data
Serial.println( " cm" );
// Code for presence detection
if ( timerRunning == 0 && distance <= 20 ){
startTime = millis() / 1000;
Serial.println("time started, starting count");
timerRunning = 1;
}
if ( timerRunning == 1 && distance >= 20 ){
endTime = millis() / 1000;
timerRunning = 0;
duration = endTime - startTime;
Serial.println ("Presence detected for seconds: ");
Serial.print(duration);
}
}
If want it to fire after the hand has been in front of the sensor for 5 seconds try this:
void loop() {
// Get distance
delay(1000);
int tofdata = sensor.readRangeContinuousMillimeters();
int distance = tofdata / 10; // convert mm to cm
// Code for presence detection
if (distance <= 20 ) {
// Object is close, check if timer is running
if (!timerRunning) {
// Timer not running. Start timer
startTime = millis();
Serial.println("time started, starting count");
timerRunning = 1;
}
else {
// Has 5 seconds passed?
uint32_t elapsed_time = millis() - startTime ;
if (elapsed_time >= 5000) {
// YES. DO SOMETHING HERE
// and reset
timerRunning = 0;
}
}
}
else {
// Object is not close.
timerRunning = 0;
}

Serial.write causing void loop program to stop (Digital Pins stop responding)

I have an issue trying to send some serial data through tx and rx to another arduino through a HC05 bluetooth module.
The overall project is developing a hybrid go kart and using the arduino as a simple ECU unit with a PID speed control over the PWM output controlling a DC motor. I have been working the project in steps and have go as far as setting up a footpedal with the arduino and controlling the electronic speed controller (ESC) directly. I have added a simple PID function to this along with a simple hall sensor to detect the speed and does require tuning but works great so far. Now the problem comes when I try to send data across over serial ports.
I have had the bluetooth modules connected with to separate arduinos and have successfully managed to send over data from one arduino with a pot input to another with a 3.5 inch TFT screen. When I try to integrate the master side of the project to the PID controlled DC motor the system freezes. I have since then removed the PID control and gone back to direct control and it still fails, i have tried commenting out the interrupt sequence for the encoder and put a static value for RPM and still freezes. the sending sequence works if I don't attempt to use any digital outputs. I am really confused. The code I have gone down to to try and debug this can be found below. This is not the full code and has been chopped to pieces to try and eliminate this fault. however in this code below, if I comment out the sendData function the system works and the motor spins with relative speed to the pedal input. as soon as I try to send the data the system runs for a seconds then freezes. the data is still being sent and the static readings are showing just the digital output seizes to work.
#include <TimerOne.h>
int previous = 0;
int Tavg = 0; // the average
int Tout = 0;
int throttle = A0;
const int numReadings = 10;
int readings[numReadings]; // the readings from the analog input
int readIndex = 0; // the index of the current reading
int total = 0; // the running total
int ESCPin = 5;
unsigned int counter=0;
int RPM;
long Time = 0;
long ReadInt = 0;
void docount() // counts from the speed sensor
{
counter++; // increase +1 the counter value
}
void timerIsr()
{
Timer1.detachInterrupt(); //stop the timer
Serial.print("Motor Speed: ");
RPM = (counter*75 ); // RPM= counterx10*60/8 (x10 for second, 8 counts in encoder, 60 minutes === 75x counter)
Serial.print(RPM);
Serial.println(" Rotation per min"); Serial.print(Tout);Serial.print("= "); Serial.print(Tout*0.01961);Serial.println("V");
counter=0; // reset counter to zero
Timer1.attachInterrupt( timerIsr ); //enable the timer
}
void ReadEnc (){
Timer1.initialize(100000); // set timer for 0.1sec
attachInterrupt(0, docount, RISING); // increase counter when speed sensor pin goes High
Timer1.attachInterrupt( timerIsr ); // enable the timer
}
void sendData(){
if (Serial.available()>0) {
if (Serial.read() == 0){
//Serial.println(sendChars);
int RPMout = RPM;
Serial.write("<");
//delay(2);
//Serial.write(sendChars);
Serial.write("Data is,");
//delay(2);
Serial.write( itoa (RPMout, 4,10));
//delay(2);
Serial.write(", 30, 48.35");
//delay(2);
Serial.write(">");
//delay(10);
Serial.println("");
}
}
}
void setup()
{
Serial.begin(9600);
pinMode(2, INPUT_PULLUP); // internal pullup input pin 2
pinMode(ESCPin, OUTPUT);
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = 0; }
Time = millis();
ReadInt = -100;
}
void ReadSensor (){
// get the sensor value
total = total - readings[readIndex];
// read from the sensor:
readings[readIndex] = analogRead(throttle);
//Serial.println(readings[readIndex]);
// add the reading to the total:
total = total + readings[readIndex];
// advance to the next position in the array:
readIndex = readIndex + 1;
// if we're at the end of the array...
if (readIndex >= numReadings) {
// ...wrap around to the beginning:
readIndex = 0;
}
// calculate the average:
Tavg = total / numReadings;
}
void loop(){
ReadSensor();
ReadEnc();
RPM = 1800;
Tout = map(Tavg, 180, 860, 0, 200);
if (Tout>0){
analogWrite(ESCPin, Tout);
}
if (Time > ReadInt + 5000) {
sendData (); // when this is commented it works fine, tried moving it everywhere
ReadInt = Time;
}
Time = millis();
}
If anyone has any ideas please let me know, and I know I probably haven't explained my problem well so if their is any questions or more details needed please ask.
Second parameter of itoa should be a pointer to output bufffer. but you do not need itoa. use Serial.print(RPM);. For string the print and write functions are the same, but for number print has a version for int

Doppler Radar analogRead()

I've been doing a project on reading doppler speed and creating velocity data. I don't have a firm background on electric engineering / signal processing but I've researched quite a bit so far. Please bear with me if I get things wrong in my explanation.
I am currently using HB100 and CDM324 breakout model. The objective in this project is to get the voltage reading from the module and create a spectrogram though FFT in MATLAB. I got a sample data from BlackCatScience with the hand moving towards the Sensor at a fast speed. Its plot is shown below:
Hand Speed Using Doppler Radar
And I've assembled my arduino kit like the picture below:
Arduino-HB100_kit
Connections are:
VCC -> 5V GND -> GND FOUT -> Pin 8 VOUT -> Pin A5
So far, I found out that HB100 supports pulse/CW usage and used the code below to measure the frequency using HB100.
#include <MsTimer2.h>
#include <FreqMeasure.h>
//--------------GlOBAL VARIALBES---------------------
double raw_data = FreqMeasure.read();
double sum = 0;
int count = 0;
double raw_data_array[10];
unsigned long timeStamp = 0;
//---------------------------------------------------
void setup() {
Serial.begin(115200);
FreqMeasure.begin();
Serial.print("Time (ms)");
Serial.print("\t");
Serial.println("Hz");
}
void loop() {
timer();
freq_measure();
}
void timer() {
timeStamp+=1;
}
void freq_measure() {
while (timeStamp < 101) {
if(FreqMeasure.available()) {
//average readings
sum += FreqMeasure.read();
count ++;
if (count > 2) {
float frequency = FreqMeasure.countToFrequency(sum/count);
// Serial.print(timeStamp);
// Serial.print("\t");
Serial.println(frequency);
timeStamp++;
sum = 0;
count = 0;
}
}
}
}
The problem with this code is that, I want to measure the voltage reading at every, say for example, 1 millisec. However, this code is similar to using pulseIn function and the rate at which the data is out differs on whether I'm moving my hand to/from the sensor or not. When I'm not moving, the data output become slow, and when I'm moving, the data output rate is fast.
Hence, I decided to use some other code and use analogRead function like below:
//-----------------------------------------------------------------
#define RADAR A5 //pin number for VOUT
//--------------------GLOBAL VARIABLES-----------------------------
unsigned long timeStamp;
//-----------------------------------------------------------------
void setup() {
Serial.begin(115200);
pinMode(RADAR, INPUT);
Serial.println(F("Settings Complete" ));
}
void loop() {
// 1 millisec == 0.001 second --> 1000 millisec == 1 second
// we want about 5 seconds of data, hence the loop runs for 5000 millisec
while (timeStamp < 5000){
showReading();
}
}
void showReading() {
// timeStamp = millis();
// Serial.print(timeStamp);
// Serial.print("\t");
//Read input on analog pin 5:
int sensorData = analogRead(RADAR);
// float voltage = sensorData * (5.0 / 1023.0);
Serial.println(sensorData);
}
This time, the serial monitor is giving me a value between 0 and 1023 and that's great, but even if I move my hand in front of the sensor, the values change but minimally. What should I do to correct this and get a plot like the graph above?
Thanks for reading such a long question. Have a great day!

implementing a state machine for servo motors

I have created a FSM for my servo. It has two states. I am using a switch/case structure but the motor is getting 'stuck' in the first case and Im not sure why.
This is my code:
#include <Servo.h>
#define one 1
#define two 2
Servo myservo1; //projector platform servo
unsigned long Timer1; //define timer variable for state 1 if statement
void setup()
{
myservo1.attach(9);
}
void loop(){
static int state = one; // initial state is one.
switch(state)
{
case one:
myservo1.writeMicroseconds(1374); // servo is moving cw
delay(5000);
myservo1.writeMicroseconds(1474); // servo is stationary
Timer1 = millis();
if (millis() - Timer1 > 5000)
{
state = two;
}
break;
case two:
for(int speedv1 = 0; speedv1 <= 100; speedv1 += 2) // loop to ramp up speed of servos
{
myservo1.writeMicroseconds(1474 + speedv1); // speed increase by 2 each iteration (servo 1) until servo reaches fullspeed (ACW)
delay(40); // delay between loop iterations
}
delay(5000);
for(int speedv2 = 0; speedv2 <= 100; speedv2 += 2) // loop to ramp down servo speed
{
myservo1.writeMicroseconds(1574 - speedv2); // speed decrease by 2 each iteration (servo 1) until servo stops
delay(40); //delay between loop iterations
}
delay(2000);
state = one;
break;
}
}
The motor seems to get stuck at
myservo1.writeMicroseconds(1374);
on the first line of case 1.
By stuck I mean the motor just continues to rotate in a clockwise fashion and does not progress to the next writeMicroseconds() statement after the delay. Appreciate the help.
With this piece of code in state one:
Timer1 = millis();
if (millis() - Timer1 > 5000)
{
state = two;
}
it's incredibly unlikely that the timer will have advance five seconds between the first and second line. Hence because you change Timer1 everyone time you run that bit of code, it will alway stay in state one.
Though it's unclear what exactly you're trying to achieve (I'm uncertain as to what the writeMicroSeconds() calls do, nor your need for the explicit delays within the states themselves), let's assume for now that you want to stay in state one for five seconds but do something else during that time. That means you need to initialise the start time as part of moving to state one, not within the state one code itself.
For example, the following code shows one way to do this, continuously running the servo clockwise for five seconds then anti-clockwise for four (with some mythical calls to start the servo running):
void loop() {
static int state = zero;
static unsigned long timer1;
switch (state) {
case zero: {
timer1 = millis();
state = one;
servo.startClockwise();
break;
}
case one: {
if (millis() - timer1 >= 5000) {
servo.stop();
servo.startAntiClockwise();
timer1 = millis();
state = two;
} else {
doSomethingElse();
}
break;
}
case two: {
if (millis() - timer1 >= 4000) {
servo.stop();
servo.startClockwise();
timer1 = millis();
state = one;
} else {
doSomethingElse();
}
break;
}
}
}

Resources