c++ - Interrupt arduino routine to run a slow delay based process -
i have arduino data collection , sends esp8266 on serial. serial communication esp not quick may know , depends on lot of waiting. have button , want stop data collection or sending , have open door. door opening takes 30 seconds. what's best way this?
not full code, goes below. of course doesn't work because can't use while or delay in isr, don't know how 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 = 0; int len = strlen(term); while (millis() < t + timeout) { if (serial2.available()) { buffer[i++] = serial2.read(); if (i >= len) { if (strncmp(buffer + - 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 complete code, can guess @ few things:
1) shouldn't wait in isr. calling millis()
discouraged, depends on timer0 isr getting called, prevented long you're in openadoor
isr.
2) in general, isr should things quick... think microseconds. that's tens hundreds of instructions, can few lines of code. digitalwrite
slow. if there's more do, should set volatile
flag watched in loop
. loop
can time-consuming work.
3) calculating elapsed time must in form:
if (millis() - starttime >= desired_time)
where starttime
same type millis()
, uint32_t
:
uint32_t starttime;
you set starttime
whereever it's appropriate:
starttime = millis();
this avoids rollover problem, when millis()
rolls on 232-1 0.
4) looks know how "block" until amount of time has elapsed: while
loop keep sketch @ point. if change if
statement, arduino can continue on way handle other things.
because loop
happens quickly, if
statement check time frequently... unless delay
or block somewhere else, wait_for_esp_response
. :-( while loop should change if
statement well. routine more check_for_esp_response
.
5) have track state of door opening , closing process. finite-state machine problem. nick has description here, too. can use enum
type define states door can in: closed, opening, opened , closing.
when open button pressed, can @ state , see if should start opening it. start timer, turn on relay and, importantly, set state opening. next time through loop
, can test state (a switch
statement), , opening case, @ time see if has been long enough. if has set state opened. , on.
if incorporate these things sketch, should start 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() { // time take sample? if (millis() - lastdatatime > data_sample_time) { lastdatatime = millis(); gathersomedata(); // may want read serial2 input first, make // sure old data doesn't mixed in new response. senddatatoesp(); senttime = millis(); waitingforresponse = true; responselen = 0; // ready new response } // if we're expecting response, did it? if (waitingforresponse) { if (check_for_esp_response("ok")) { // got it! lightgreenled(); waitingforresponse = false; } else if (millis() - senttime > 2000ul) { // long! lightredled(); waitingforresponse = false; } // else, still waiting } // check , handle door open , close buttons, // based on current door state , time switch (doorstate) { case door_closed: if (dooropenpressed) { digitalwrite(dooropenrelay, low); relaychanged = millis(); doorstate = door_opening; } break; case door_opening: // has door been opening long enough? if (millis() - relaychanged > door_open_time) { digitalwrite(dooropenrelay, high); doorstate = door_opened; } else if (!dooropenpressed && doorclosepressed) { // oops, changed mind , pressed close button. // may want calculate relaychanged time // set millis() based on how long // door has been opening. if started opening, // don't want drive relay // 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()) { // should make sure you're not running off 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 don't block or delay anywhere. loop
gets called on , over, , checks few variables. of time, there's nothing do. sometimes, based on state or current time, gathers data, sends it, reads response, , opens or closes door. these actions not interfere each other, because there no blocking while
loops, quick checks if
statements.
Comments
Post a Comment