One interrupt function to modify a dynamically determined instance? - c++

As usual, I know how to bypass this problem with some ugly patch work, but I want to make it elegant: I would like to make a small wrapper for motors commanded by an Arduino, which would unfortunately mean writing a distinct interrupt routine for each instance of motor because it has to modify the right step counter (member variable of the motor class) to determine its rate. However those functions would obviously have the same processing...
My question is: how can I determine which counter to modify in a unique interrupt routine?
Here is what I have so far, I'd like to hide interrupts from the user.
class Motor {
volatile int counter;
unsigned long lastUpdate; //Last timestamp update (for calculating the rate)
static const unsigned int RESOLUTION = 1024; //Number of steps in one rev
static const unsigned int RPMS_TO_RPM = 60000; //Convers rev/ms to rpm
public:
Motor() : counter(0)
{
lastUpdate = millis();
}
void encoderInput(bool pinA, bool pinB)
{
counter += (pinA ^ pinB)*(-1)+!(pinA ^ pinB);
}
int getRate() {
int ret = float(counter)/RESOLUTION/(millis() - lastUpdate)*RPMS_TO_RPM;
lastUpdate = millis();
counter = 0;
return ret;
}
};
/* Example:
* Motor motor1;
*
* void motor1_isr(void) {
* motor1.encoderInput(PIN_A, PIN_B);
* }
*
* void setup() {
* attachInterrupt(PIN_I, motor1_isr, CHANGE);
* Serial.begin(9600);
* }
*
* void loop() {
* Serial.println(motor1.getRate());
* delay(1000);
* }
*/
Thanks for your help, I think it would be useful to other people as well once it's done with :)
Regards,
Mister Mystère

You are facing a fundamental problem: an ISR is called with no data. In other words, the ISR function is not provided any data that indicates its source of the interrupt.
The basic approach is that the person writing the code provides the linkage between input and action by hardcoding a function. The tricky approach you are looking for is a method by which the ISR can figure out what the source was.
So you want to have N motors with 2N inputs from quadrature encoders. A single interrupt handler is attached to all the input pins on a CHANGE condition. The same handler gets called for all N motors. It can figure out which Motor to update by comparing the input pins to the values the last time it was called. If the input pins changed, then call that motor. Here is a psuedo code
Motor motor1;
Motor motor2;
onSomethingChanged() {
static int in1aprev, in1bprev;
static int in2aprev, in2bprev;
int in1a, in1b;
int in2a, in2b;
in1a = digitalRead(....
same for other in's
if( (in1a!=in1alast) || (in1b!=in1blast)) {
motor1.encoderInput(in1a,in1b);
in1alast = in1a;
in1blast = in1b;
}
if( (in2a!=in2alast) || (in2b!=in1blast)) {
motor2.encoderInput(in2a,in2b);
in1a2ast = in2a;
in1b2ast = in2b;
}
return;
}
Not so long ago this type of function would be handled in an entire chip (see programmable interrupt controller ). The chip implements all the logic to trigger and capture the source of the interrupt. The main CPU just gets a trigger "something happened". The handler polls the chip to ask "what happened".
Having offered this method, I'm not sure I would recommend. You cannot hide what is going on with your code. The motor must be physically wired to the correct pins. You have consumed a scarce resource -- you have to tell people.

Related

Arduino include a library inside another library using Visual Studio

Hi guys I need help with my project.
I don't understand how to include a library to another library that I'm building for my project. I'm writing a library Encoders.h able to control movements and button click of my encoders. To read the movement I use the famous Encoder.h library that you can find here.
Here my code
Encoders.h
#ifndef ENCODER_h
#define ENCODER_h
#include <Arduino.h>
#include <Timer.h>
#include <joyconf.h>
#include <Encoder.h>
class Encoders_ {
private:
// one encoder input
int _clk;
// the other encoder input
int _dt;
// click push button
int _sw;
// read state of sw
int _sw_read;
// last sw state
int _sw_last_state;
// current state encoder pin A
int _current_clk;
// current state encoder pin B
int _current_dt;
// last state pin A
int _last_clk;
// last state pin B
int _last_dt;
// last direction state
int _last_res;
//last saved direction result
int _res;
//ready to encode?
int _ready;
// timer class to debounce clicks
Timer_ _Timer1;
// timer class to debounce directions
Timer_ _Timer2;
// set pinA and pinB in the external lib
Encoder _Myenc(uint8_t pina, uint8_t pinb);
//Encoder _Myenc;
public:
/**
* to initialize the encoder with input pins and click pin.
* All af them are digital inputs.
*/
Encoders_(int clk, int dt, int sw);
/**
* give the direction (one step change)
*
* #param how long time give the same output before reset to 0 output
*
* #return 1 if clockwise (right) or 0 if no changes or -1 if anticlock-wise (left)
*/
int direction(long out_t);
/**
* return the SW push button state. It is debounced to avoid false clicks.
* Please check the mechanical condition of every encoder.
* Some encoder can need longer delay.
*
* #param long deboucing time
*
* #return int 0 LOW (clicked) or 1 HIGH (released).
*/
int click(long deb_time);
/**
* return the last direction state of the encoder
*
* #return int 1 (right), -1 (left).
*/
int lastState();
};
#endif
Encoders.cpp
#include <Encoders.h>
#include <Encoder.h>
Encoders_::Encoders_(int clk, int dt, int sw){
_Myenc(clk,dt);
_clk = clk;
_dt = dt;
_sw = sw;
_last_clk = HIGH;
_last_dt = HIGH;
_current_clk = HIGH;
_current_dt = HIGH;
_sw_last_state = HIGH;
_last_res = 0;
_ready = 0;
}
int Encoders_::direction(long out_t){
long newPosition = _Myenc.read();
newPosition;
}
int Encoders_::click(long deb_time){
// read the state of the switch into a local variable:
// normal open state is HIGH
_sw_read = digitalRead(_sw);
/*if(_sw_read) Serial.println("HIGH");
else Serial.println("LOW");*/
/*save the state before timer check to not stuck on a
specific state*/
_sw_last_state = _sw_read;
//Serial.println(_sw_last_state);
if(_Timer1.expired(deb_time)){
_Timer1.update();
if (!_sw_read){
return 0;
} else {
return 1;
}
}
return _sw_last_state;
}
int Encoders_::lastState(){
return _res;
}
Please note that Encoders with "s" is my library and without "s" is the external library.
In long newPosition = Myenc.read(); I get the error "'Myenc' was not declared in this scope" and "expression must have class type"
I don't understand how can I include Encoder library and initialize it with two parameters. I'm following the same logic of my Timer.h library but is not the same (in Timer_ my constructor is empty and this make everything easy)
Thanks so much for helping me.
_Myenc is a function, not an object. You probably meant to write _Myenc().read() rather than _Myenc.read();.

Arduino Uno to Particle Photon - Converting Code

Please bear with me. I am an amateur programmer at Arduino, and have never used Particle photon before.
I used an Arduino Uno and wrote the attached code that detects heart beat and temperature using the Grove Ear clip heart beat sensor and the Grove Temperature sensor and then prints them in the console every 20 seconds. Previously, this code was written to show in an Grove OLED screen but later simplified it back to using it without the OLED screen.
I am now looking into using the same application and function using the same sensor on a Particle Photon (as it is smaller and has WiFi capability). I have never previously used this technology before but I saw online that it, more or less, uses the same code application. I have been through the sample codes for this online available on its website but I have no idea how to convert my code for the Arduino into code for the photon (Photon console compiles the code without any error but does not show any sensor data). Can someone either point me to the right direction/ appropriate online resources / help me change this code here to make it work on the Photon? (I just added the Particle.publish() wherever I was using Arduino's Serial.println() but it still doesn't print anything).
The result in the console shows:
Please be ready
This will now begin
then it prints number 1 to 20 every second followed by sensor readings.
Again sorry for the inconvenience and thank you for your help.
I used direct codes from the documentation blog of the sensors (bottom of the page of the linked page):
Grove heart bear sensor
Grove Temperature sensor
#define LED 4//indicator, Grove - LED is connected with D4 of Arduino
boolean led_state = LOW;//state of LED, each time an external interrupt
//will change the state of LED
float tempa;
int tempPin = 0;
unsigned char counter;
unsigned long temp[21];
unsigned long sub;
bool data_effect=true;
unsigned int heart_rate;//the measurement result of heart rate
const int max_heartpluse_duty = 2000;//you can change it follow your system's request.
//2000 meams 2 seconds. System return error
//if the duty overtrip 2 second.
void setup()
{
pinMode(LED, OUTPUT);
Serial.begin(9600);
while (!Serial){
;
}
Serial.println("Please be ready");
delay(5000);
arrayInit();
Serial.println("This will now begin.");
attachInterrupt(0, interrupt, RISING);//set interrupt 0,digital port 2
}
void loop()
{
digitalWrite(LED, led_state);//Update the state of the indicator
}
/*Function: calculate the heart rate*/
void sum()
{
if(data_effect)
{
heart_rate=1200000/(temp[20]-temp[0]);//60*20*1000/20_total_time
Serial.print("Heart_rate_is:\t");
Serial.println(heart_rate);
tempa = analogRead(tempPin);
tempa = tempa * 0.11;
Serial.print("Body Temperature = ");
Serial.print(tempa);
Serial.print("*C");
Serial.println();
delay(1000);
}
data_effect=1;//sign bit
}
/*Function: Interrupt service routine.Get the sigal from the external interrupt*/
void interrupt()
{
temp[counter]=millis();
Serial.println(counter,DEC);
switch(counter)
{
case 0:
sub=temp[counter]-temp[20];
break;
default:
sub=temp[counter]-temp[counter-1];
break;
}
if(sub>max_heartpluse_duty)//set 2 seconds as max heart pluse duty
{
data_effect=0;//sign bit
counter=0;
Serial.println("measurement error,test will restart!" );
arrayInit();
}
else if (counter==20&&data_effect)
{
counter=0;
sum();
}
else if(counter!=20&&data_effect)
{
counter++;
}
else
{
counter=0;
data_effect=1;
}
}
/*Function: Initialization for the array(temp)*/
void arrayInit()
{
for(unsigned char i=0;i < 20;i ++)
{
temp[i]=0;
}
temp[20]=millis();
}

Values initialized and set outside interrupt are zero within interrupt C++ on Xilinx Zynq

I am experiencing a rather odd issue with my code where I have a value that I initialize in the main. Once I try to call that variable inside an interrupt it is 0. What am I missing here? Do different variables live within an interrupt?
I would like to note that this is a more theoretical question about interrupts and whether I can not share values between idle and interrupts.
Supervisor supervisor;
XScuTimer TimerInstance;
XScuGic IntcInstance;
uint32_t offset;
void interruptRoutine(void *CallBackRef) {
// Define pointer to timer
XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
// If timer is expired, clear interrupt status
if (XScuTimer_IsExpired(TimerInstancePtr)) {
XScuTimer_ClearInterruptStatus(TimerInstancePtr);
// Why is offset 0 within the interrupt routine?
xil_printf("Interrupt: %u\n", offset);
}
}
//.. I omitted the interrupt init method ..//
int main() {
while (true) {
// Get all sensor data
offset = 1;
}
}

Interrupt arduino routine to run a slow delay based process

I have an arduino that does mostly data collection and sends it to an ESP8266 over serial. Serial communication to the ESP is not quick as you may know and it depends on a lot of waiting. I have a button and I want to immediately stop any data collection or sending and have it open a door. The door opening takes about 30 seconds. What's the best way to do this?
Not the full code, but it goes something like the below.
Of course this doesn't work because you can't use WHILE or DELAY in an ISR, but I don't know how to restructure it.
attachInterrupt(4 , openadoor, FALLING);
void loop(){
gathersomedata();
senddatatoESP();
if(wait_for_esp_response(2000,"OK")) lightGreenLED();
else lightRedLED();
}
byte wait_for_esp_response(int timeout, const char* term) {
unsigned long t = millis();
bool found = false;
int i = 0;
int len = strlen(term);
while (millis() < t + timeout) {
if (Serial2.available()) {
buffer[i++] = Serial2.read();
if (i >= len) {
if (strncmp(buffer + i - len, term, len) == 0) {
found = true;
break;
}
}
}
}
buffer[i] = 0;
}
void openadoor(){
while (doortimer + dooropentime >= millis() && digitalRead(openbutton) == HIGH && digitalRead(closebutton) == HIGH) {
digitalWrite(DoorOpenRelay, LOW);
}
digitalWrite(DoorOpenRelay, HIGH);
}
TL;DR - see Nick's Answer. :-)
Without the complete code, I can only guess at a few things:
1) You shouldn't wait in an ISR. Even calling millis() is discouraged, as it depends on the Timer0 ISR getting called, which will be prevented as long as you're in your openadoor ISR.
2) In general, the ISR should only do things that are very quick... think microseconds. That's tens to hundreds of instructions, which can be just a few lines of code. Even digitalWrite is almost too slow. If there's more to do, you should just set a volatile flag that is watched in loop. Then loop can do the time-consuming work.
3) Calculating elapsed time must be in this form:
if (millis() - startTime >= DESIRED_TIME)
where startTime is the same type as millis(), a uint32_t:
uint32_t startTime;
You set startTime whereever it's appropriate:
startTime = millis();
This avoids the rollover problem, when millis() rolls over from 232-1 to 0.
4) It looks like you know how to "block" until a certain amount of time has elapsed: the while loop will keep your sketch at that point. If you just change it to an if statement, the Arduino can continue on its way to handle other things.
Because loop happens so quickly, the if statement will check the time very frequently... unless you delay or block somewhere else, like wait_for_esp_response. :-( That while loop should change to an if statement as well. The routine is more like check_for_esp_response.
5) You have to track the state of the door opening and closing process. This is a Finite-State machine problem. Nick has a good description here, too. You can use the enum type to define the states that the door can be in: CLOSED, OPENING, OPENED and CLOSING.
When the OPEN button is pressed, you can look at the state and see if you should start opening it. Then start a timer, turn on the relay and, most importantly, set the state to OPENING. Next time through loop, you can test the state (a switch statement), and for the OPENING case, look at the time to see if it has been long enough. If it has set the state to OPENED. And so on.
If I incorporate all these things into your sketch, it should start to look like this:
volatile bool doorOpenPressed = false;
volatile bool doorClosePressed = false;
static const uint32_t DOOR_OPEN_TIME = 30000UL; // ms
static const uint32_t DOOR_CLOSE_TIME = 30000UL; // ms
static const uint32_t DATA_SAMPLE_TIME = 60000UL; // ms
static uint32_t lastDataTime, sentTime, relayChanged;
static bool waitingForResponse = false;
static uint8_t responseLen = 0;
enum doorState_t { DOOR_CLOSED, DOOR_OPENING, DOOR_OPENED, DOOR_CLOSING };
doorState_t doorState = DOOR_CLOSED;
void setup()
{
attachInterrupt(4 , openadoor, FALLING);
}
void loop()
{
// Is it time to take another sample?
if (millis() - lastDataTime > DATA_SAMPLE_TIME) {
lastDataTime = millis();
gathersomedata();
// You may want to read all Serial2 input first, to make
// sure old data doesn't get mixed in with the new response.
senddatatoESP();
sentTime = millis();
waitingForResponse = true;
responseLen = 0; // ready for new response
}
// If we're expecting a response, did we get it?
if (waitingForResponse) {
if (check_for_esp_response("OK")) {
// Got it!
lightGreenLED();
waitingForResponse = false;
} else if (millis() - sentTime > 2000UL) {
// Too long!
lightRedLED();
waitingForResponse = false;
} // else, still waiting
}
// Check and handle the door OPEN and CLOSE buttons,
// based on the current door state and time
switch (doorState) {
case DOOR_CLOSED:
if (doorOpenPressed) {
digitalWrite(DoorOpenRelay, LOW);
relayChanged = millis();
doorState = DOOR_OPENING;
}
break;
case DOOR_OPENING:
// Has the door been opening long enough?
if (millis() - relayChanged > DOOR_OPEN_TIME) {
digitalWrite(DoorOpenRelay, HIGH);
doorState = DOOR_OPENED;
} else if (!doorOpenPressed && doorClosePressed) {
// Oops, changed their mind and pressed the CLOSE button.
// You may want to calculate a relayChanged time that
// is set back from millis() based on how long the
// door has been opening. If it just started opening,
// you probably don't want to drive the relay for the
// full 30 seconds.
...
}
break;
case DOOR_OPENED:
if (doorClosePressed) {
...
}
break;
case DOOR_CLOSING:
if (millis() - relayChanged > DOOR_CLOSE_TIME) {
...
}
break;
}
}
void openadoor()
{
doorOpenPressed = true;
}
bool check_for_esp_response(const char* term)
{
bool found = false;
if (Serial2.available()) {
// You should make sure you're not running off the end
// of "buffer" here!
buffer[responseLen++] = Serial2.read();
int len = strlen(term);
if (responseLen >= len) {
if (strncmp(buffer + responseLen - len, term, len) == 0) {
found = true;
}
}
}
return found;
}
The key is that you don't block or delay anywhere. loop gets called over and over, and it just checks a few variables. Most of the time, there's nothing to do. But sometimes, based on the state or the current time, it gathers some data, sends it, reads the response, and opens or closes the door. These actions do not interfere with each other, because there are no blocking while loops, only quick checks with if statements.
Open the door in the ISR and set a flag. Also store the time when you opened it. Both of those variables should be declared volatile.
Then in your main loop see if:
The flag is set; and
Time is up
If so, close the door (and clear the flag).
May I assume that setting the variables as "volatile" will prevent the compiler optimizing them? If so, then would you mind explaining why you thought this necessary.
Variables modified inside an ISR may change when the compiler does not expect them to. Using volatile tells the compiler to reload such variables from RAM (and not cache them into a register) so it always gets the most up-to-date copy.
Just as an example, say you had a flag set inside an ISR. And in your main (non-ISR) code you had this:
flag = false;
while (!flag)
{ } // wait for flag to be set
The compiler looks at that and thinks "well, flag will never change" and optimizes away the test for it changing. With volatile though, the compiler keeps the test, because it has to keep reloading flag from RAM.

Arduino: interrupt expensive function and resume another

As a first project I plan on making a teensyduino ambient light with different light modes, which are checked in a big switch statement - now I want to switch from one mode to another by pressing a button.
googling lead me to using interrupts, but there is one point that is not clear - if I press the button during an expensive function, which takes long time and has many variables in use, what happens if i call the main loop from the interrupt, does the remaining state of remain in the ram and leads to a stackoverflow if I do switch too many times or is it cleared.
Here some code:
const int speed = 30 //milliseconds
const int modes = 11; //maximum number of modes
const int red = 15;
const int green = 14;
const int blue = 12;
volatile int mode = 0;
void setup() {
pinMode(red , OUTPUT);
pinMode(green , OUTPUT);
pinMode(blue , OUTPUT);
randomSeed(analogRead(0));
Serial.begin(9600);
attachInterrupt(0,incMode,CHANGE); // 0 -> digital pin 2
}
void loop() {
switch(mode){
case 0:{
Serial.println("powerdown");
setAll(0);
delay(1000);
break;
}
\\...
case modes:{
\\ expensive long function
}
}
}
void blinkAll(int times){
for(int i=1;i <= times;i++){
setAll(255);
delay(speed*17);
setAll(0);
delay(speed*17);
}
}
void setAll(int bright){
analogWrite(red , bright);
analogWrite(green , bright);
analogWrite(blue , bright);
}
void incMode(){
delay(speed);
blinkAll(2); //to indicate mode has changed
mode = (mode+1) % (modes+1); //switch starts with 0 so use "% modes+1"!
Serial.println("mode increased");
//--> loop();
//--> would resume the main loop but lead to a stackoverflow i presume
}
How would I break out of the running function without delay and stack pollution.
I know I could just set the mode and wait until the function has ended, but if I have a mode that takes minutes to end I want to be able to switch from it immediately.
PS.: Though I am using a teensyduino, I will use the arduino tag, and as I don't know what language the arduinio uses the tags c/c++. Please change this if it is not appropriate.
You would eventually overflow the stack if you were to reenter main from the interrupt handler multiple times recursively. Additionally, since you'll still be in the interrupt handler as far as the hardware is concerned, you'll have all kinds of weirdness - in particular, interrupts are blocked when you're already in an interrupt, which means delay() won't work and millis() won't count up, and various other things will be broken as well unless you figure out some way to manually re-enable interrupts.
A better way to solve this would be to make your 'expensive long function' instead be a state machine driven by a cheap, short function that is called very frequently. Your interrupt handler can then simply set a flag that is checked on entry into this function, at which point the current mode (ie, current state machine) is changed.
This approach also makes it easier to define new lighting modes. For example, you could define something like this:
struct phase {
unsigned char r, g, b, delay;
};
unsigned long t_nextPhase;
volatile struct phase *forceMode = NULL;
struct phase *mode = blinkAll;
int nextPhase = 0;
struct phase blinkAll[] = {
{ 255, 255, 255, 17 },
{ 0, 0, 0, 17 },
{ 0, 0, 0, 255 } // loop sentinel
};
void lighting_kernel() {
noInterrupts(); // ensure we don't race with interrupts
if (forceMode) {
mode = forceMode;
forceMode = NULL;
t_nextPhase = millis();
nextPhase = 0;
}
interrupts();
if (t_nextPhase > millis()) {
return;
}
struct phase *cur_phase;
do {
cur_phase = mode[nextPhase++];
if (cur_phase->delay == 255) {
nextPhase = 0;
}
} while (cur_phase->delay == 255);
analogWrite(red , cur_phase->r);
analogWrite(green , cur_phase->g);
analogWrite(blue , cur_phase->b);
t_nextPhase = millis() + cur_phase->delay;
}
Now to define a new lighting mode you just need a new array of colors and times, rather than writing new code. Adding things like color ramps and other such effects is left as an exercise to the reader.