Click here to Skip to main content
15,868,016 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm writing an IoT thing and the issue I'm having is that the technique I'm using to timeout and then reset that timeout is not working. It's a technique I've used many times in the past, and I'm posting here just to rule out that I've got something wrong in this code. The behavior I'm getting is unexpected.

What I am doing is using millis() which returns the number of milliseconds elapsed since the machine was booted. I'm storing that in a timestamp, and then in my loop() function which gets called for each iteration of a tight loop the cpu spins for me basically I'm seeing if the number of the difference between now and when the timestamp was taken is more than 1000 milliseconds (1 second)

I have a callback() method that is called any time the end of a wire is physically touched. This works, as in the routine fires, but it's behaving as if I didn't reset the timestamp inside the function.

Can anyone tell me where I went wrong?

(See the "what have you tried?" section for more)

What I have tried:

C++
uint32_t _timestamp;

// gets called when wire is touched
void callback() {
  // millis() won't advance in interrupt routines
  _timestamp = 0; // reset the timeout
  // equiv of _timestamp=millis();
  Serial.println("Callback");

}

// runs once on CPU startup
void setup() {
  Serial.begin(115200);

  // attach interrupt on GPIO15 touchpad
  // with to 40 sensitivity. callback()
  // gets fired when wire is touched
  touchAttachInterrupt(T3, callback, 40);
  // reset the timeout
  _timestamp = millis();
}

// called for each iteration of the CPU's loop
void loop() {
  // for callback():
  if (!_timestamp)
    _timestamp = millis();

  // if more than a second has elapsed write it
  if (millis() - _timestamp > 1000) {
    // and reset the timestamp
    _timestamp = millis();
    Serial.println("Time elapsed");
  }
}


Above I've bolded a critical line that appears not to be running. I've also tried making _timestamp volatile to no avail.

The serial output (with timestamps) are as follows


This yields
15:29:56.544 -> Time elapsed
15:29:57.537 -> Time elapsed
...
(i'm pressing the touch sensor wire now)
15:29:59.755 -> Callback
15:29:59.755 -> Time elapsed
15:29:59.788 -> Callback
15:29:59.788 -> Time elapsed
15:29:59.821 -> Callback
....
(i've released the touch sensor wire)
15:30:00.186 -> Time elapsed
15:30:01.179 -> Time elapsed
...


I've bolded some bits to highlight how fast it's happening.

but anyway I expect something like this
Time elapsed
Time elapsed 
Time elapsed
(I've held the wire)
Callback
Callback
Callback
Callback
(I've released the wire)
Time elapsed 
Time elapsed
Posted
Updated 13-Nov-20 1:23am
v2
Comments
Rick York 13-Nov-20 0:39am    
Is this single threaded? I get the impression it is but I wanted to verify.
honey the codewitch 13-Nov-20 4:47am    
Ummmm, ish? AFAIK all the code seems to be single threaded, and yet the thing is dual core. Also it was said I needed to use volatile on global vars accessed within interrupts like callback which suggests some sort of concurrent access I think. I didn't mark _timestamp with volatile above but i have to no avail.
Rick York 13-Nov-20 12:21pm    
It seems the atomic lock helped fix things. My first thought was a 'threading' issue and concurrent access. Hence, my question.
markkuk 13-Nov-20 5:31am    
You could try the official ESP32 Arduino forum: https://esp32.com/viewforum.php?f=19 for more expertise.
honey the codewitch 13-Nov-20 8:32am    
I'm a member of the ESP32 forum on reddit. I looked there first but didn't see anyone posting code, so I figured I'd try here first. Wasn't sure how they'd feel about blowing up their forum with all that source.

Try this:
C++
#include <atomic>

uint32_t _timestamp;
std::atomic_flag touched;

// gets called when wire is touched
void callback() {
  touched.clear(); // reset the timeout
  Serial.println("Callback");

}

// runs once on CPU startup
void setup() {
  Serial.begin(115200);
  touched.test_and_set();
  // attach interrupt on GPIO15 touchpad
  // with to 40 sensitivity. callback()
  // gets fired when wire is touched
  touchAttachInterrupt(T3, callback, 40);
  // reset the timeout
  _timestamp = millis();
}

// called for each iteration of the CPU's loop
void loop() {
  // for callback():
  if (!touched.test_and_set())
    _timestamp = millis();

  // if more than a second has elapsed write it
  if (millis() - _timestamp > 1000) {
    // and reset the timestamp
    _timestamp = millis();
    Serial.println("Time elapsed");
  }
}
 
Share this answer
 
Comments
CPallini 13-Nov-20 8:00am    
5.
honey the codewitch 13-Nov-20 8:23am    
I'm not sure the standard library is available on arduino's devkit but i'll try this. Thanks.
markkuk 13-Nov-20 11:07am    
Arduino for ESP32 runs on top of ESP-IDF, so you get the G++ compiler, FreeRTOS and other stuff not available with traditional AVR based Arduino boards.
honey the codewitch 13-Nov-20 11:17am    
That's great to know. I am still brand new to the ESP32, and fairly new to the Arduino and ESP8266 modules as well, but I've at least built several things with the arduino, and ESP8266s so far.
honey the codewitch 13-Nov-20 8:25am    
BRILLIANT! Thanks so much!
Quote:
_timestamp = 0; // reset the timeout
// equiv of _timestamp=millis();
Well, you didn't reset the timeout, in the callback.
The remark is wrong. As matter of fact, the correct way is
C
_timestamp = millis();


I actually missed the check in the main loop.

However your code doesn't handle properly the possibility of the interrupt routine returning after such check is performed.
 
Share this answer
 
v2
Comments
honey the codewitch 13-Nov-20 4:43am    
Read the code and read the comments in the code. millis() doesn't work in that routine

If you see loop() routine

// for callback():
  if (!_timestamp)
    _timestamp = millis();


That indeed resets the timeout. The comment is correct.
CPallini 13-Nov-20 5:12am    
Well, I missed that check in the main loop. However, your code assumes that such a check would be performed immediately after the return from the interrupt routine. Such an assumption is wrong.
honey the codewitch 13-Nov-20 5:15am    
It doesn't assume that, or rather, the assumption is safe, since loop() would simply skip the timeout check on that iteration.

Also, for the sake of this question, consider the code single threaded.
markkuk 13-Nov-20 5:20am    
Interrupt callbacks can be called any time, not just between calls to loop(). Any code using interrupts isn't really single threaded.
honey the codewitch 13-Nov-20 11:20am    
I know it's kind of not, because Ints can interrupt anywhere, but (maybe not with the ESP32) when an interrupt is called the other code is suspended, which is why i said it's single threaded for the purpose of the code. I may have been making a bad assumption about the value of timestamp, but above is what i meant by consider this single threaded - there's no other code running at the same time, but yes i know it can pull an interrupt anywhere.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS


CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900