Skip to content

Instantly share code, notes, and snippets.

@asaaki
Created January 30, 2019 09:30
Show Gist options
  • Save asaaki/b3613343cb68528b3f46f51900132349 to your computer and use it in GitHub Desktop.
Save asaaki/b3613343cb68528b3f46f51900132349 to your computer and use it in GitHub Desktop.
motion sensor / interrupt triggered neopixel light controller
#include <Adafruit_NeoPixel.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#define STRIP_PIN 6
#define TRIGGER_PIN 2
#define CONTROL_LED 13
// Note: this duration should be also longer than the HIGH time of the PIR
const uint32_t lightDuration = 9 * 1000L; // ms
Adafruit_NeoPixel strip = Adafruit_NeoPixel(1, STRIP_PIN, NEO_GRB + NEO_KHZ800);
void setup() {
digitalWrite(TRIGGER_PIN, LOW);
digitalWrite(CONTROL_LED, LOW);
pinMode(TRIGGER_PIN, INPUT);
pinMode(CONTROL_LED, OUTPUT);
// warm up phase for the PIR sensor
for(int i = 1; i <= 120; i++) {
digitalWrite(CONTROL_LED, HIGH); delay(100);
digitalWrite(CONTROL_LED, LOW); delay(100);
}
}
void loop() {
interrupts();
lightshow();
hibernate();
}
// ISR; set state only, never do heavy lifting here
// noop - we only need to resume from where we left off; see further down in hibernate()
void wakeup() {}
void lightshow() {
// I don't trust the sleep mode state, so I re-init the strip with each wake up call, works fine
strip.begin();
strip.setBrightness(100);
strip.show();
for(uint32_t tStart = millis(); (millis() - tStart) < lightDuration; /**/) {
// short tasks (less than max duration time ideally)
rainbow(10);
}
strip.clear();
strip.show();
digitalWrite(CONTROL_LED, HIGH);
delay(500);
digitalWrite(CONTROL_LED, LOW);
}
void hibernate() {
// set all pins (except our control LED) to inputs (considered best practice for sleep mode)
for(int pin = 0; pin < 20; pin++) { if(pin != CONTROL_LED) { pinMode(pin, INPUT); } };
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // saves a lot of power, other modes are maybe less effective
ADCSRA &= ~(1 << 7); // disable ADC (saves some power)
sleep_bod_disable(); // disable BOD (saves power, maybe?)
// where and how do we want to be awaken?
attachInterrupt(digitalPinToInterrupt(TRIGGER_PIN), wakeup, CHANGE);
sleep_mode();
// ... sleep and wait for interrupt to happen ...
// interrupt fired; resume ...
sleep_disable();
detachInterrupt(digitalPinToInterrupt(TRIGGER_PIN));
}
// stolen from the neopixel example
void rainbow(uint8_t wait) {
uint16_t i, j;
for(j=0; j<256; j++) {
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i+j) & 255));
}
strip.show();
delay(wait);
}
}
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
@asaaki
Copy link
Author

asaaki commented Jan 30, 2019

Keep in mind: the continuation point after wake-up depends on the microcontrollers. Arduinos/AVRs resume where they stopped, so after the sleep_mode(); line.
But others like ESP handle sleep/wake-ups a bit different (starting with setup() again), so you have to check how the controller was reset.
Check the docs for the sleep mode of your µC.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment