Saturday, July 25, 2020

0000 0000 0011 1001

Tripping the light - fantastic!


After some gentle (?) banter regarding the size and number of buckets of electronics about the place, it was suggested to me by a long time collaborator that the door at the bottom of the staircase into the garage could use some incidental lighting (e.g. before the main lights at the wall are flicked on).

A normal person presumably would go to a hardware store and spend $20 and 15 minutes to fix the problem. Clearly I'm not normal, and so I started a project which could either be labelled:
  1. Lighting the garage upon detecting a human presence, or
  2. Justification (in part) for the many buckets of electronics clogging up the place
The first order of business was planning the physical elements of the project (see video) and then a quick (and dirty) circuit diagram showing the relevant parts orchestrated to achieve the desired outcome.


At the heart of the project is an ATTiny13 (of course) which is asleep. If the door from the stairwell is opened then a hall effect sensor is tripped and the μC wakes up and brings up the LED strip via a transistor. Alternatively from the opposite direction if a warm humanoid stands in front of a PIR then the μC also trips and provides light. I've chosen components that use very little current and so overall the circuit consumes around 500μA when "asleep".


AM312 Mini-PIR at ~15μA

Hall Effect sensor at ~1μA

Voltage regulator at ~1μA 

I then mocked up a prototype, wrote some code and soldered up the "PCB of Goodness".



// -----------------------------------------------------------------
// Description: A strip of Leds connected to a transistor controlled
// by an Attiny13. The μC sleeps most of the time, but is interrupted
// by a PIR on PCINT1, or a Hall Effect sensor on PCINT2. It will then
// light the leds, hold for a bit and and then fade out.
//
// Author: OneCircuit      Date: Thursday 16 July 11:48:26 AEST 2020
// www.onecircuit.blogspot.com
// www.youtube.com/channel/UCTm3eCrN6wk-Ozt9ymyKIIg
// -----------------------------------------------------------------
//
// ATMEL ATTINY13 μC
//
//                                   +-\/-+
//  RESET--ACD0--5/A0--PCINT5--PB5  1|    |8  VCC
//   CLKI--ACD3--3/A3--PCINT3--PB3  2|    |7  PB2--PCINT2--2/A1--SCK--ADC1
//         ACD2--4/A2--PCINT4--PB4  3|    |6  PB1--PCINT1---1---MISO--OCOB--INT0*
//                             GND  4|    |5  PB0--PCINT0---0---MOSI--OCOA*
//                                   +----+
//  * indicates PWM port
//

#include <avr/pgmspace.h>            // for reading the progmem values
#include <avr/interrupt.h>           // for interrupt routines
#include <avr/sleep.h>               // the sleep routines

#define StripLED PB0                 // pwm pin
#define HEPin PCINT2                 // Magnetic interrupt pin
#define PIRPin PCINT1                // PIR interrupt pin

volatile boolean triggered = false;  // boolean for keeping track of interrupts

// The led strip will fade exponentially to compensate for human perception,
// which is not linear. You can calculate as a curve, but float values will blow
// the little Attiny13 out of the water, so a lookup table in progmem makes more
// sense for saving memory.

const uint8_t PROGMEM gamma8[] = {
  0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4,
  5, 5, 6, 6, 7, 8, 8, 9, 9, 10, 11, 11, 12, 13, 14, 14, 15,
  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
  30, 31, 32, 34, 35, 36, 37, 39, 40, 41, 43, 44, 46, 47,
  48, 50, 51, 53, 54, 56, 58, 59, 61, 63, 64, 66, 68, 69,
  71, 73, 75, 77, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96,
  98, 100, 102, 105, 107, 109, 111, 113, 116, 118, 120,
  123, 125, 127, 130, 132, 134, 137, 139, 142, 144, 147,
  150, 152, 155, 157, 160, 163, 165, 168, 171, 174, 176,
  179, 182, 185, 188, 191, 194, 197, 200, 203, 206, 209,
  212, 215, 218, 221, 224, 227, 230, 234, 237, 240, 243, 247, 250
};

void slowfade(boolean fadein) {

  if (fadein) {
    for (uint8_t lighting = 0; lighting < 150; lighting++) {    // 150 steps
      analogWrite(StripLED, pgm_read_byte(&gamma8[lighting]));  // read from the lookup table
      delay(6);                                                 // increase for a slower fade
    }
  }
  else {
    for (uint8_t lighting = 150; lighting > 0; lighting--) {    // count backwards
      analogWrite(StripLED, pgm_read_byte(&gamma8[lighting]));  // from the lookup table
      delay(80);                                                // increase for a slower fade
    }
  }
}

void powerDown(void)
{
  GIMSK |= (1 << PCIE);                   // activate Pin change interrupts
  PCMSK |= ((1 << HEPin)|(1 << PIRPin));  // sets the Pin change interrupt mask
  ADCSRA &= ~(1 << ADEN);                 // turn off ADC
  ACSR |= (1 << ACD);                     // turn off Analog comparator.
  sei();                                  // enable global interrupts
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // sleep deeply little one
  sleep_enable();                         // enable sleep mode
  sleep_mode();                           // system sleeps here

  sleep_disable();                        // ISR routine returns here so wake up
  GIMSK &= ~(1 << PCIE);                  // deactivate Pin change interrupts
  ADCSRA |= (1 << ADEN);                  // turn on ADC
  ACSR = (0 << ACD);                      // turn on Analog comparator.
  delay(100);                             // settle time after waking up
  
}

ISR(PCINT0_vect)
{
  triggered = true;
}

void setup ()
{
  pinMode(HEPin, INPUT_PULLUP);    // configure interrupt pin as input
  pinMode(PIRPin, INPUT_PULLUP);   // configure interrupt pin as input
  pinMode(StripLED, INPUT);        // LED pin input to start

}  // end of setup

void loop () {
  if (!triggered) {
    powerDown(); // call the function that sleeps
  }
  else {
    pinMode(StripLED, OUTPUT);    // LED pin is now output
    slowfade(true);               // fade in
    delay(60000);                 // hold that thought for 1 minute
    slowfade(false);              // fade out
    pinMode (StripLED, INPUT);    // configure transistor pin as input, saves power
    delay(1000);                  // one second before re-trigger
    triggered = false;            // reset boolean
  }
}  // end of code

Finally after testing the new device was installed and currently it lights up when either the door is swung open, or a human is detected via PIR. Mission 1 outlined above - accomplished. Mission 2 - ongoing...



No comments:

Post a Comment