I'm facing a programming question in which I want to trigger some code whenever a capacitive touch sensor has been touched for 100 ms (to distinguish false positives in my prototype). My sensor is touched by this code
if (digitalRead(touchPin))
Now whenever it has been touched for 100ms I want some other code (for instance, activating a LED) to run. I can't really seem to find a solution because my startTime = millis() variable keeps resetting.
Does anyone know how to tackle this problem?
You need a bool variable, to store last state (TRUE if touched and FALSE if not)
Also, you need to store time when it has been changed to TRUE. Time could be taken by millis() function
If your bool variable is true, check, if time passed is more than your 100 ms.
So:
// In your global scope:
...
// Last touch state
bool isTouched = FALSE;
// time, when last touch happened
int touched_t = 0;
// In your loop:
...
bool isTouchedNow = (digitalRead(touchPin) == HIGH);
// Touch state is changed till last measure:
if (isTouchedNow != isTouched)
{
// Set "last isTouched state" to new one
isTouched = isTouchedNow;
// If it wasn't touched before, store current time (else zero):
touched_t = isTouched ? millis() : 0;
}
else //If touch state isn't changed till last time:
{
//If state was "touched" and now it "touched", and 100ms has passed:
if (isTouched && touched_t > 0 && millis() - touched_t > 100)
{
// Call your function, that should be called,
// whan sensor is touched for 100 ms (activate a LED of something)
DOTHESTUFF();
}
}
...
Related
I am using an ESP32 connected to Google Cloud IoT Core to control lights with a raspberry pi as a hub to send messages to the esp32 through IoT core to turn them on and off at set times of the day.
What I want to do now is just eliminate the raspberry pi and have the esp32 manage when it needs to turn them on or off but I cant see to find a way to set alarms or something like alarms that trigger at specific times of days.
The time value coming in is in milliseconds from UTC
Does something like that exists or how could I accomplish alarms on the esp32?
I don't know if you are using the ESP IDF or some sort of Arduino framework for the ESP32 (with which I am not familiar). This answer is for the ESP IDF. If you are not using the ESP-IDF and don't have access to the relevant header files and libraries, some of the ideas might still be relevant.
Activate SNTP
Ensure that the clock on the controller is updated by following the SNTP example at https://github.com/espressif/esp-idf/tree/master/examples/protocols/sntp .
Use localtime()
If you are using the ESP-IDF you will have access to the localtime_r() function. Pass this (a pointer to) the number of 'seconds since the Epoch' - as returned by time(NULL) - and it will fill a struct tm with the day of the week, day of the month, hour, minute and second.
The general procedure is (error handling ommitted) ...
#include <time.h>
// ...
struct tm now = {};
time_t tnow = time(NULL);
localtime_r(&tnow, &now);
// Compare current day, hour, minute, etc. against pre-defined alarms.
Note: You can use gmtime_r() instead of localtime_r() if you prefer to use UTC.
You can invoke this periodically from your main while() loop as outlined below.
Alarm structures
Define a structure for your alarm information. You could use something like the following - tweaking according to the level of granularity you need. Note that it includes a callback member - to specify what the program should actually do when the alarm is triggered.
typedef struct {
int week_day; // -1 for every day of week
int hour; // -1 for every hour
int minute; // -1 for every minute
void (*callback)(void);
void *callback_arg;
} alarm_t;
Define alarms and callbacks
Define your array of alarms, callback functions and arguments.
static void trigger_relay(const void *arg) {
uint32_t num = *(uint32_t *)arg;
printf("Activating relay %u ...\n", num);
// Handle GPIO etc.
}
static uint32_t relay_1 = 0;
static uint32_t relay_2 = 1;
static alarm_t alarms[] = {
// Trigger relay 1 at 9:00 on Sunday
{0, 9, 0, trigger_relay, (void *)&relay_1},
// Relay 2 at 7:30 every day
{-1, 7, 30, trigger_relay, (void *)&relay_2},
};
Periodic scan
Define a check_alarms() routine which loads a struct tm structure with the current time and compares this with each of the alarms you have defined. If the current time matches that of the alarm, the callback is invoked.
static void check_alarms(void) {
time_t tnow = time(NULL);
struct tm now = {};
localtime_r(&tnow, &now);
ESP_LOGI(TAG, "Time: %u:%u:%u", now.tm_hour, now.tm_min, now.tm_sec);
size_t n_alarms = sizeof(alarms) / sizeof(alarms[0]);
for (int i = 0; i < n_alarms; i++) {
alarm_t *alrm = &alarms[i];
if (alrm->week_day != -1 && alrm->week_day != now.tm_wday) {
continue;
}
if (alrm->hour != -1 && alrm->hour != now.tm_hour) {
continue;
}
if (alrm->minute != -1 && alrm->minute != now.tm_min) {
continue;
}
alrm->callback(alrm->callback_arg);
}
}
This would then be called from your main while() loop with a frequency depending on the granularity of your alarm. Since the alarm_t defined above has a granularity of 1 minute, that is how often I will call check_alarms(). This example is for FreeRTOS but could be adapted as needs be.
while (1) {
TickType_t tick = xTaskGetTickCount();
if (tick - g_time_last_check > pdMS_TO_TICKS(60000)) {
g_time_last_check = tick;
check_alarms();
}
}
Efficiency
The above is not very efficient if you have a lot of alarms and / or each alarm should be triggered at high frequency. In such a case, you might consider algorithms which sort the alarms based on time until elapsed, and - instead of iterating over the entire array every time - instead employ a one-shot software timer to invoke the relevant callback. It probably isn't the worth the hassle unless you have a very large number of timers though.
Alternatives
Both FreeRTOS and the ESP-IDF include APIs for software timers - but these can not be (easily) used for alarms at a specific time of the day - which you state as a requirement. On the other hand - as you may already know - the ESP32 system-on-a-chip does have a built-in RTC, but I think it is best to use a higher-level interface than communicating with it directly.
A real-time clock is what you are looking for. I've never worked with the ESP32 but apparently it has one and it is bad. However, 5% error might be good enough for you. If it's an option, I would use an external one.
I have one scenario problem and I don't want to use interrupts cause the same problem will be there I will be forced to use delay to insure that specific time did pass.
the problem:
I don't want to use the delay function cause it stops the other tasks to be checked or executed taking a situation:
I have motor that run 6 seconds if button1 is pressed and if a man press button2 the motor 2 immediately need to be turned on and work for 2 second.
so the code will be like this:
main ()
{
if( Rd0 == 0 ) // is button1 is pressed
{
RunMotre1();
__delay_ms(6000);
StopMotor1();
}
if( Rd1 == 0 ) // is button2 is pressed
{
RunMotor2();
__delay_ms(2000);
StopMotor2();
}
}
the problem of this code if we pressed the button b to start Motor 2 he will
not work cause the system is blocked on delay and wait it to be finished .
and that cause that if we start -> press button 1->motor 1 runing -> pressing buTTon2 to start Motor2-> Motor2 not working until the 6 seconds be passed.
so is there a way to resolve this problem case.
for example if exists comparing who mutch time Motor1 did run if is greate or = 6 secound then stop the motor 1 example (samthing like this if exist) :
Time counter1;
Time Counter2;
main()
{
if( Rd0 == 0 ) // is button 1 to start motor1 is presed ?
{
RunMotre1();
counter1.start();
}
if(counter1.CurentElapsedTimes==6 Secound) // is motor1 did run 6 seconds
{
counter1.stop();
counter1.Initialise();
StopMotor1();
}
if( Rd1 == 0 ) // is button 2 to start motor2 is pressed.
RunMotor2();
counter2.start();
}
if(counter2.CurentElapsedTimes==2 Secound) // is motor2 did run 2 seconds
{
counter2.stop();
counter2.Initialise();
StopMotor2();
}
or any solution
platform pic microcontroller using xc8 compiler
PRESUPPOSITION: I don't know which PIC you are using, but I assume that it has at least a free timer.
The problem here is that you need something to track the time as it passes. My suggestion is to use the same technique that the Arduino FW uses: setup a timer to fire an interrupt every X milliseconds/seconds, then in the interrupt service routine (ISR) update a counter.
When you press the button set the counter, then wait for the counter to reach another value before turning the motor off.
I'll write here a small pseudo-code example, since I don't remember how to properly write an ISR with a PIC compiler nor I know what is your PIC (and, so, the commands to initialize the timer).
I chose to use a timer granularity of 100ms, but you can change it as you prefer.
I also chose to set counter as a 8-bit variable; it can count up to 25.5 seconds, but since the highest delay is 6 seconds it's ok.
Note that you need just ONE timer, so you can add any number of motors without the need for adding counters
char counter;
char motor1StartTime;
char motor2StartTime;
bool motor1Running;
bool motor2Running;
Timer1_ISR()
{
counter++;
}
main()
{
counter = 0;
motor1StartTime = 0;
motor2StartTime = 0;
motor1Running = false;
motor2Running = false;
// Setup the timer to trigger the Timer1_ISR every 100ms
...
while(1)
{
if( Rd0 == 0 )
{ // If the button is pressed start the motor and save the counter
RunMotor1();
motor1StartTime = counter;
motor1Running = true;
}
if ((motor1Running) && ((counter - motor1StartTime) >= 60))
{ // If the motor is running from more than 6 seconds, shut it down
StopMotor1();
motor1Running = false;
}
if( Rd1 == 0 )
{ // If the button is pressed start the motor and save the counter
RunMotor2();
motor2StartTime = counter;
motor2Running = true;
}
if ((motor2Running) && ((counter - motor2StartTime) >= 20))
{ // If the motor is running from more than 2 seconds, shut it down
StopMotor2();
motor2Running = false;
}
}
}
What I'd like to accomplish is for the button to be called once after I press it with my fingers. Sometimes it works but there are also times it doesn't. Let's say I need to select from a menu. There are times that when I press down or up button, it moves perfectly but sometimes, it will move twice with a single press. I'd like to get that issue fixed.
Somewhere in global:
int debounceDelay = 50;
The code inside the loop
a3StateDownButton = digitalRead(A3);
if (a3StateDownButton != a3DownButtonLastState) {
a3DownButtonLastDebounceTime = millis();
}
if ((millis() - a3DownButtonLastDebounceTime) > debounceDelay) {
if (a3StateDownButton != currenta3ButtonState) {
currenta3ButtonState = a3StateDownButton;
if (currenta3ButtonState == HIGH) {
isDownButtonPressed = true;
// do what ever you need to do when button is high
} else if (currenta3ButtonState == LOW) {
isDownButtonPressed = false;
}
}
}
a3DownButtonLastState = a3StateDownButton;
The button I am using is very similar to this, almost exactly the same.
I only have a resistor connected to one of the pins but I forgot the value I put, most likely 2.2k.
So again, sometimes it's good but not constantly perfect. I'm also thinking that playing with the value of debounceDelay might affect my menu, which I remember it did. The response became slower when the value was increased. I think this is called software debouncing. Maybe there is something I can add to make it a hardware debouncing.
I am trying to program 2 buttons (w/ an Arduino), one which will start a 5 second cycle and one which can stop the cycle at any point within this time frame. When the "Start" button is pressed "START" is printed to the serial monitor, and if the cycle is allowed to complete, "DONE" will be printed as well, followed by a new line. If the "stop" button is pressed, "STOP" is printed to the serial monitor and the cycle is terminated. At least, in theory this is how it should work. What I'm finding is that after I press the start button about 5 times, even if both buttons function perfectly beforehand, at this point the "DONE" will never be printed to the serial monitor. The last thing printed will be the "START" from the most recent button press. The only way to end this is to press the "stop" button, which prints "STOP." However, after pressing the "start" button after this, the code prints a rapid succession of "START\nDONE" couplets. Can anyone see a problem with the code I have? I thought it was a pretty straightforward task, and maybe it's just a problem with the hardware, but if it is a problem with the code, I can't see it.
int startPin = 4;
int stopPin = 7;
int motorPin = 2;
boolean startState = false;
boolean stopState = false;
void setup()
{
pinMode(startPin, INPUT);
pinMode(stopPin, INPUT);
pinMode(motorPin, OUTPUT);
Serial.begin(9600);
Serial.println("setup");
}
void loop()
{
startState = digitalRead(startPin);
if(startState == false)
{
return;
}
Serial.print("START\n");
int time = millis() + 5000;
while(millis() < time)
{
stopState = digitalRead(stopPin);
if(stopState == true) {
Serial.print("STOP\n");
return;
}
analogWrite(motorPin, 255);
}
analogWrite(motorPin, 0);
Serial.print("DONE\n\n");
}
As a final note- this is my first time using Stack Overflow, so I apologize in advance if this isn't a good question, or if it's already been answered, or if I'm doing something else that will appear overwhelmingly stupid to someone more experience than me. And thank you to anyone who can help me.
To start, change
int time = millis() + 5000;
To
unsigned long time = millis() + 5000;
millis() returns an unsigned long so if you are trying to insert that number + 5000 into a signed int, you are overflowing the int and causing weird calculations in your loop.
I need to power on my a/c when the temperature is lower than a number and viceversa power off it when the temperature become higher than another number. So I've tried this sketch
if (DHT.temperature,1 >= 0 && DHT.temperature,1 <=18)
irsend.sendRaw(ON, sizeof(ON)/sizeof(int),khz);
else if
irsend.sendRaw(OFF, sizeof(OFF)/sizeof(int),khz);
but it send the OFF command every loop cycle given that the temperature don't change immediately. Is possible to send the ON and OFF command only once for cycle and wait that it change for send the other command? I've thinked to store the last sent command but I don't know how to do. Thanks
You haven't written your if statement properly. Also add a test for if it is on already. You can do it like this:
This will have to be a global variable so it doesn't get destroyed outside your loop below.
boolean isOn = false;
This can go directly into your void loop() or another other function so you can call it as needed.
if (tempCondition && (!isOn)){
irsend.sendRaw(ON, sizeof(ON)/sizeof(int),khz);
isOn = true;
}
else if (othertempCondition && isOn) {
irsend.sendRaw(OFF, sizeof(OFF)/sizeof(int),khz);
isOn = false;
}
This will test the temperature and if true it will check if it needs to toggle using the isOn boolean.
If you want to supply the temperature ranges I can help complete the temp condition portions if you need it.