Reading signal with interrupts using arduino - arduino

I am trying to read an input signal from a source that is a PWM signal. In my research I have found some useful articles out found here: http://www.instructables.com/id/Arduino-Frequency-Detection/?ALLSTEPS and here: http://www.camelsoftware.com/2015/12/25/reading-pwm-signals-from-an-rc-receiver-with-arduino/. The first article is a little beyond my experience level, and would not be helpful if I used anything other then a uno, although it seems to perform extremely well. The second shows a method I was better able to understand.
I have been using the following code somewhat successfully:
#define input_pin 2
volatile unsigned long timer_start;
volatile int pulse_time;
volatile int last_interrupt_time;
void calcSignal()
{
last_interrupt_time = micros();
if(digitalRead(input_pin) == HIGH)
{
timer_start = micros();
}
else {
if(timer_start != 0)
{
//record the pulse time
pulse_time = ((volatile int)micros() - timer_start);
//restart the timer
timer_start = 0;
}
}
}
void setup()
{
timer_start = 0;
attachInterrupt(0, calcSignal, CHANGE);
Serial.begin(115200);
}
void loop()
{
Serial.println(pulse_time);
delay(20);
}
The problem with this setup for my application is the interrupt is only triggered by a change in state, when realistically I need to know the duration of how long it is high and how long it is low. A picture of the ideal signals can be seen here with various duty cycles (https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM). I tried changing the interrupt mode from CHANGE to LOW and HIGH, but did not get any creditable results, as it only output zeros on the serial monitor. Is there something I am missing or an alternative library/method that can be used? I am somewhat new to programming, so I have some understanding, but am by no means a programmer.

What about something like this:
unsigned long timeout = 100000L; // reasonable timeout around 100ms?
unsigned long high_us = pulseIn(pinNo, HIGH, timeout);
unsigned long low_us = pulseIn(pinNo, LOW, timeout);
unsigned long thousandths = (high_us*1000) / (high_us+low_us);
However it won't be able to measure one period HI/LO. It'll be measured over two of them: >HI< lo hi >LO<.
And if it's not what you want, you can dig into AVR DataSheet and try to get Timer Input Capture interrupts working.

I think you can go with your method, just by adding another timer:
void calcSignal() {
if(digitalRead(input_pin) == HIGH)
{ // transition from LOW to HIGH
timerH_start = micros();
//record the pulse time
pulse_time_L = ((volatile int)micros() - timerL_start);
}
else { // transition from HIGH to LOW
timerL_start = micros();
//record the pulse time
pulse_time_H = ((volatile int)micros() - timerH_start);
}
}

Related

Enable esp32 deep sleep mode after a certain period of inactivity

I have a device designed in esp that contains two buttons, each of which performs a function. However, he is constantly monitoring these buttons and running out of battery. How do I implement esp's deep sleep, after a certain period of inactivity? For example, if the device does not read in 30 seconds, activate sleep mode. To exit the mode, just press any of the existing buttons.
I thought of something from the team, but it’s not working, because the milis() parameter is never reset.
void loop()
{
unsigned long timer=millis();
int botaoRfid = digitalRead(butRFID);
int botaoDig = digitalRead(butDIG);
if(botaoRfid == HIGH){
timer=0;
Serial.println("botao RFID pressionado");
checkRFID();
}
else if(botaoDig == HIGH){
timer=0;
Serial.println("botao DIGITAL pressionado");
checkFingerprint();
return;
}
else if(timer >=10000){
Serial.println("Sleep…");
esp_deep_sleep_start();
}
}
Thanks!
It is not really a problem, that millis() is not reset after startup, when you rewrite your code like so:
unsigned long timer = 0;
void setup()
{
}
void loop()
{
int botaoRfid = digitalRead(butRFID);
int botaoDig = digitalRead(butDIG);
if(botaoRfid == HIGH){
timer = millis();
Serial.println("botao RFID pressionado");
checkRFID();
}
else if(botaoDig == HIGH){
timer = millis();
Serial.println("botao DIGITAL pressionado");
checkFingerprint();
return;
}
else if(millis() - timer >= 10000){
Serial.println("Sleep…");
esp_deep_sleep_start();
}
}
This would put the ESP32 into deep sleep after 10 seconds without a reset to the current millis() (which is currently happening in every loop cycle, but you can of course put that code outside the loop...). Beware however, that millis() will overflow after about 49 days and you will have to handle this correctly.
Also in order to wake the ESP32 from DeepSleep you have to define a PIN to listen on using for example esp_sleep_enable_ext1_wakeup(bitmask, mode). There are also other option outlined here.

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.

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

Arduino Interrupt won't ignore falling edge

I'm having some trouble completely debouncing a button attached to to an interrupt. The goal is to have the statement in the void loop() run exactly once when the button is pressed/released.
What usually ends up happening is one of two things
The ISR flag is set once when the button is pressed. Releasing the button does nothing, as intended.
The ISR flag is set once when the button is pressed, and once more when the button is released.
Here is the exact code I have:
#define interruptPin 2
#define DBOUNCE 100
volatile byte state = LOW; //ISR flag, triggers code in main loop
volatile unsigned long difference;
void setup() {
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
Serial.begin(115200);
}
void loop() {
if(state){ //If we have a valid interrupt
Serial.println(difference); //Print the time since the last ISR call
state = LOW; //Reset the flag
}
}
void ISR_0() {
static unsigned long last_interrupt = 0;
if(millis()-last_interrupt > DBOUNCE && digitalRead(interruptPin)){
difference=millis()-last_interrupt;
state = HIGH;
}
last_interrupt = millis(); //note the last time the ISR was called
}
This seems to be a popular way to debounce an interrupt, but for whatever reason it isn't working for me.
I was hoping on the first falling edge of the button release that digitalRead(interruptPin) would read low, so the state flag would not be set.
Since the ISR updates the last_interrupt time, the successive bounces after the first falling edge still seem to be successfully ignored. This leads me to believe the debouncing is not the issue, but the digitalRead(interruptPin) is.
The debouncing seems to take care of all but one state. When the button is released, the code still occasionally sets the state flag to HIGH.
Here's some sample output:
3643 (after waiting ~3.6 seconds from boot, I press the button, releasing it ~1 second later)
In the same scenario as above, the output occasionally looks like this:
3643
1018
This shows me pressing the button, but also releasing the button.
I'm using an UNO R3 and a momentary tactile push button with a 1k pull-down resistor.
I'm not sure what's going wrong at this point. I hope this is simple enough that anyone can easily test this on their arduino if they feel so inclined.
You can always do debouncing in the hardware
I've encountered bounce several times with buttons and encoders, and doing debouncing in the software can get messy and lead to unreadable code or logic errors.
The easiest thing you can do is add a 0.1 uF capacitor, as illustrated:
The Arduino has hysteresis inputs, and if you use 10K as your pullup then this works for bounce that is under 1ms. This is my favorite approach.
If you wanna get more serious, a wonderful pdf exists on the internet with lots of examples and explanations: A Guide to Debouncing
You use the internal pullup resistor of the Uno, but also a pulldown resistor on the button itself. That's not correct, you can only use one of of them.
Inside the if, you want the input to be high, so use the pulldown resistor and change INPUT_PULLUP into INPUT.
(to make things clear: the resistor is connected between input pin and ground, the button between input pin and +5V)
[edit]
I think when the ISR is called, the status of interruptPin might be changed again.
Due to the slowness of digitalRead, compared to the (possible) spike.
I'm not sure if this is what you want, but the example below is working.
(I've enabled the LED for testing purposes).
One thing: keep the button pressed at least the debounce time (100msec), otherwise it won't work.
Not ideal, but that's the price you have to pay if you want instantly response on a switch.
#define interruptPin 2
#define DBOUNCE 100
volatile byte state = LOW; //ISR flag, triggers code in main loop
volatile unsigned long difference;
bool hasPrinted = false;
void setup() {
pinMode(interruptPin, INPUT);
pinMode (13, OUTPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
Serial.begin(115200);
}
void loop() {
digitalWrite(13, state);
if (!hasPrinted) {
Serial.println(difference);
hasPrinted = true;
}
}
void ISR_0() {
static unsigned long last_interrupt = 0;
if(millis()-last_interrupt > DBOUNCE){
state = !state;
hasPrinted = false;
difference = millis()-last_interrupt;
}
last_interrupt = millis(); //note the last time the ISR was called
}
Debouncing buttons/switches in interrupts is a hassle.
I faced a (sort of) similar situation with limit switches.
The moment a limit switch is hit, something has to occur - so interrupt.
However the interrupt would fire when the limit switch was released as well which was a problem. I had my interrupt set to fire on a FALLING edge.
Anyhow, I wound up debouncing outside of the interrupt, using flags.
The code explains it but:
Switch is hit, ISR runs (does what it needs to) and sets an ISR flag.
The ISR flag stops the ISR from actually doing anything until it's cleared.
In the main loop, call a debounce function if the ISR flag is set.
the debounce function will wait until a pin/switch is stable at the required state (HIGH/LOW) for a predefined time, then clear ISR flag, allowing the ISR to do something again.
#define interruptPin 2
#define DEBOUNCE_TIME 100L
volatile bool ISR_ACTIVATED = false;
volatile bool display_int_time = false;
bool debounce_started = false;
unsigned long Isr_debounce_pin_timer;
volatile unsigned long difference;
void setup() {
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
Serial.begin(112500);
}
void loop() {
if (display_int_time) {
Serial.println(difference); //Print the time since the last ISR call
// Right, done with interrupt stuff. clear the interrupt flag
display_int_time = false;
}
// Call debounce ISR routine in main loop
if (ISR_ACTIVATED)
ISR_ACTIVATED = !debounce(interruptPin, HIGH, Isr_debounce_pin_timer);
}
bool debounce(int debounce_pin, bool state, unsigned long &state_latch_start) {
// debounce_pin - what pin are we debouncing?
// state - should the stable state be HIGH or LOW
// state_latch_start - when we 'latched' on to a (possibly) stable state
static bool current_state;
// Read the required pin
current_state = digitalRead(debounce_pin);
// Is the pin at the required state, and we have not yet 'latched' it?
if ((!debounce_started) && (current_state == state)) {
// 'latch' this state (ie take note of when the pin went to the required level)
state_latch_start = millis();
debounce_started = true;
}
// Have we 'latched', but the pin has bounced
if (debounce_started && (current_state != state))
// unlatch
debounce_started = false;
// Have we latched, the pin is at the required level and enough time has passed (ie pin is stable)
if (debounce_started && (current_state == state) && (((unsigned long)(millis() - state_latch_start)) >= DEBOUNCE_TIME)) {
// cool. unlatch
debounce_started = false;
// report back that all is goood.
return(true);
}
// Blast. Either the pin is at the wrong level, or is still bouncing. Tell the boss to try again later!
return(false);
}
void ISR_0() {
static unsigned long last_interrupt = 0;
if((millis()-last_interrupt > DEBOUNCE_TIME) && digitalRead(interruptPin) && !ISR_ACTIVATED){
difference=millis()-last_interrupt;
//state = HIGH;
ISR_ACTIVATED = true;
display_int_time = true;
}
last_interrupt = millis(); //note the last time the ISR was called
}
*** Just a note, edited my code to incorporate the debounce time in your original ISR to ensure that the interrupt is valid. I had not read the comment regarding your noisy environment
I suppose the main issue, as was said in the comments, is because you have no actual debounce.
So, after the button is released, it continues to bounce, causing logic level on the input pin to change. If at the moment, when the pin state is latched for the digitalRead(), the state was read as high, then the whole condition will be met and state = HIGH; will be performed.
I'm not an artist, but I did my best to draw this timing diagram:
So, to avoid this, you can utilize any simple approach to debounce. Most simple one is to read again the pin state after a little timeout, greater than the maximal expected bounce time.
For example, if you're waiting for the button to be pressed and got high logical level (or low if the button is connected to GND), just wait about 5ms and read the level again. Process the button press only if the level still high (low).
And as was said in the other answers, hardware debounce also will help. You can use a higher resistor (actually you need not use external resistor: connect the button to GND and enable the internal pull-up, which is about 35kOhm). And add a capacitor about 1nF, parallel to the button.
My final solution, thanks to #darrob
#define interruptPin 2
#define DEBOUNCE_TIME 50L
//ISR Flags
volatile bool ISR_DEACTIVATED = false;
volatile bool display_int_time = false;
bool debounce_started = false;
unsigned long Isr_debounce_pin_timer;
int x = 0;
void setup() {
pinMode(interruptPin, INPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
Serial.begin(112500);
}
void loop() {
//This runs every time we press the button
if (display_int_time) {
Serial.print("X "); //Print the time since the last ISR call
x++;
(x%10==0)?Serial.println(x):Serial.println();
display_int_time = false; //Done with interrupt stuff. Clear the interrupt flag
}
//Debounce for the ISR routine in main loop
if (ISR_DEACTIVATED)
//Wait until the pin settles LOW to reactivate the ISR
ISR_DEACTIVATED = !debounce(interruptPin, LOW, Isr_debounce_pin_timer);
}
//Returns TRUE if pin is stable at desired state, FALSE if bouncing or other state
bool debounce(const int debounce_pin, const bool state, unsigned long &state_latch_start) {
// debounce_pin - what pin are we debouncing?
// state - should the stable state be HIGH or LOW
// state_latch_start - when we 'latched' on to a (possibly) stable state
//If you are calling this routine to debounce multiple pins in the same loop,
// this needs to be defined outside of the function, and passed in as a separate
// parameter for each debounce item (like unsigned long &state_latch_start)
static bool current_state;
// Read the required pin
current_state = digitalRead(debounce_pin);
// Is the pin at the required state, and we have not yet 'latched' it?
if ((!debounce_started) && (current_state == state)) {
// 'latch' this state (ie take note of when the pin went to the required level)
state_latch_start = millis();
debounce_started = true;
}
// Have we 'latched', but the pin has bounced
if (debounce_started && (current_state != state))
// unlatch
debounce_started = false;
// Have we latched, the pin is at the required level and enough time has passed (ie pin is stable)
if (debounce_started && (current_state == state) && (((unsigned long)(millis() - state_latch_start)) >= DEBOUNCE_TIME)) {
// cool. unlatch
debounce_started = false;
// report back that all is goood.
return(true);
}
//Either the pin is at the wrong level, or is still bouncing. Try again later!
return(false);
}
void ISR_0() {
if(!ISR_DEACTIVATED){
ISR_DEACTIVATED = true;
display_int_time = true;
}
}

Motion controlled light

I'm trying to create an motion controlled light that doesn't turn on unless it's dark.
I want the light to turn on and off slowly with pwm when motion is detected. Right now my code works and does almost everything I want it to.
The problem is that I'm using the delay function to keep the light on after it reaches full brightness. And because of this if motion is detected while the light is on it doesn't reset the timer. Once the delay timer runs out and motion is detected the brightness will go down and then back up.
I would like to have the motion detector reset the timer that keeps the led on but haven't been able to get it working.
I have looked at these:
http://playground.arduino.cc/Code/AvoidDelay
https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
But because of my somewhat limited understanding of arduino code I haven't been able to get it working. I would really appreciate any help.
SOLVED
The working code below:
const byte MotionPin = 4; // Motionsensor
int LightSensorPin = 2; // Lightsensor
const byte LedPin = 11; // LED
unsigned long MotionDetectedMillis; // When motion was detected
unsigned long ledTurnedOnAt; // when led was turned on
unsigned long turnOnDelay = 50; // wait to turn on LED
unsigned long turnOffDelay = 5000; // turn off LED after this time
bool ledReady = false; // Has motion been detected
bool ledState = false; // Led on or off
int LightSensorState = 0;
int fadeAmount = 5; // How much to fade in a loop
byte brightness = 0;
void setup() {
pinMode(MotionPin, INPUT);
pinMode(LightSensorPin, INPUT);
pinMode(LedPin, OUTPUT);
digitalWrite(LedPin, LOW);
}
void loop() {
// get the time at the start of this loop()
unsigned long currentMillis = millis();
if (digitalRead(MotionPin) == HIGH && digitalRead(LightSensorPin)== HIGH)
{
MotionDetectedMillis = millis();
ledReady = true;
}
if (ledReady)
{
if ((unsigned long)(currentMillis - MotionDetectedMillis) >= turnOnDelay)
{
while (brightness < 255)
{
brightness = brightness + fadeAmount;
analogWrite(LedPin, brightness);
delay(30);
}
// setup our next "state"
ledState = true;
// save when the LED turned on
ledTurnedOnAt = currentMillis;
ledReady = false;
}
}
// see if we are watching for the time to turn off LED
if (ledState)
{
if ((unsigned long)(currentMillis - ledTurnedOnAt) >= turnOffDelay)
{
while (brightness >0)
{
brightness = brightness - fadeAmount;
analogWrite(LedPin, brightness);
delay(30);
}
ledState = false;
}
}
}
what you want is a state machine; you have to keep track in the loop of which state you currently are and what is the condition to trigger next state; because your loop will execute an action only if something happen to trigger a new state, instead of using delay() you will use a 'waiting for timeout' state (remember to keep a way to know what is the next state).
If this confuses you, imagine each state as step in the instruction manual;
turn on led 1, wait x, turn on led 1 a bit more, wait y, etc..
And because you have no more delay, before or after manage the "normal flow" of the state, you can add some special case like "if input 2 is high and current state is X or Y then set current state to Z".
So, what you want could be easily archived with an interrupt, but with a simple project like this one, you can get the same result just by removing the delay(OnTime) function.
If you want more on the interrupts of the atmega328 which is the one the Arduino UNO uses, go here

Resources