Close

NTLamp Source Code

A project log for NTLamp

Touchless dimming lamp with PIR activated clockface and sunrise alarm.

Jeremy LambertJeremy Lambert 02/07/2015 at 21:110 Comments

Code dump:

/*  
	NTLamp2
	Written by Jeremy Lambert
	Copyright 2015 All Rights Reserved

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 2.1 of the License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.
*/

//NOTE: DST compensation is in with 24 hour bug fix.  set RTC with standard time.

#include <Wire.h> 
#include <Adafruit_NeoPixel.h>
#include <Adafruit_LEDBackpack.h>
#include <Adafruit_GFX.h>
#include <avr/power.h>
#include <RTClib.h>
#include <Timer.h>

#include "enums.h"

#define SENSOR_PIN_IRD        3 //analog
#define SENSOR_PIN_PIR        7 //digital
#define LIGHT_PIN             5 //digital...changed this from 4 because I think the timer is on there
#define NUMPIXELS             16 //neopixel ring
#define DEBOUNCE_DELAY        10
#define SUNRISE_DELAY         6000 //should generally be 6000 (6 secs a color click = 30 min sunrise)
#define SUNRISE_TICK_COUNT    300

boolean dimmerActive = false;
int val1 = 0;
int val2 = 0;
int val3 = 0;
int val4 = 0;

//DIMMER CONTROL
int lastPWM = 0;
int smoothPWM = 20;
int readingPWM = 0;
int currentLightPWM = 0;
int DIMMER_STEP = 4;

boolean clockfaceLEDState = false;

uint8_t sunrise_index = 0;
uint8_t mode = INACTIVE;
int tickEvent = -1;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, LIGHT_PIN, NEO_GRB + NEO_KHZ800);
RTC_DS1307 RTC;
DateTime Clock;
Timer t;
Adafruit_7segment matrix = Adafruit_7segment();

//uint8_t redList[] = {  0,8,17,26,35,43,52,61,70,79,87,96,105,114,123,131,140,149,158,167,175,184,193,202,211,219,228,237,246,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254,253,253,252,252,251,250,250,249,249,248,247,247,246,246,245,245,244,243,243,242,242,241,240,240,239,239,238,237,237,236,236,235,235,234,233,233,232,232,231,230,230,229,229,228,228,227,226,226,225,225,224,223,223,222,222,221,220,220,219,219,218,218,217,216,216,215,215,214,213,213,212,212,211,211,210,209,209,208,208,207,206,206,205,205,204,203,203,202,202,201,201,200,199,199,198,198,197,196,196,195,195,194,193,193,192,192,191,191,190,189,189,188,188,187,186,186,185,185,184,184,183,182,182,181,181,180,179,179,178,178,177,176,176,175,175,174,174,173,172,172,171,171,170,169,169,168,168,167,167,166,165,165,164,164,163,162,162,161,161,160,159,159,158,158,157,157,156,155,155,154,154,153,152,152,151,151,150,150,149,148,147,146,145,144,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128
//};
//uint8_t greenList[] = {  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,13,15,17,19,21,23,26,28,30,32,34,36,39,41,43,45,47,49,52,54,56,58,60,62,65,67,69,71,73,75,78,80,82,84,86,88,91,93,95,97,99,101,104,106,108,110,112,114,117,119,121,123,125,128,128,130,132,135,137,140,142,145,147,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,149,148,147,146,145,144,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128
//};
//uint8_t blueList[] = {  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,5,6,7,9,10,11,12,14,15,16,18,19,20,22,23,24,25,27,28,29,31,32,33,34,36,37,38,40,41,42,44,45,46,47,49,50,51,53,54,55,57,58,59,60,62,63,64,66,67,68,69,71,72,73,75,76,77,79,80,81,82,84,85,86,88,89,90,92,93,94,95,97,98,99,101,102,103,104,106,107,108,110,111,112,114,115,116,117,119,120,121,123,124,125,127,128,129,130,132,133,134,136,137,138,139,141,142,143,145,146,147,149,150,151,152,154,155,156,158,159,160,162,163,164,165,167,168,169,171,172,173,174,176,177,178,180,181,182,184,185,186,187,189,190,191,193,194,195,197,198,199,200,202,203,204,206,207,208,209,211,212,213,215,216,217,219,220,221,222,224,225,226,228,229,230,232,233,234,235,236,237,238,240,241,242,243,244,245,246,247,248,249,250,251,252,253,255
//};

void setup() {
  
  Serial.begin(9600);
  Serial.println("start setup");
  pinMode(SENSOR_PIN_IRD, INPUT);
  Serial.println("start IRD");
  pinMode(SENSOR_PIN_PIR, INPUT);
  Serial.println("start PIR");
  digitalWrite(SENSOR_PIN_PIR, LOW); //no pullup. no need.
  Wire.begin();
  Serial.println("start wire");
  pixels.begin();
  Serial.println("start pixels");
  
  RTC.begin();
  
  //RTC.adjust(DateTime(F(__DATE__), F(__TIME__))); //this is for setting the clock with the compile date and time.  kinda handy.
  //TODO: don't forget to comment out for the final push to device, or it'll reset to whatever time it was compiled every time it's reset
  
  Serial.println("start RTC");
  matrix.begin(0x70);
  Serial.println("start matrix");
  matrix.setBrightness(3);
  Serial.println("set brightness");
  startUp();
  Serial.println("startup");
  setColors(0, 0, 0);
  Serial.println("set lights off");
  Serial.println("end setup");
}

void loop() 
{
  t.update(); //timer tick update
  //clock functions
  uint8_t hourval =0, minuteval = 0, secondval = 0;
  if(digitalRead(SENSOR_PIN_PIR) == HIGH && !dimmerActive)
  {
    //Serial.println("face on");
    DateTime clock; 
    clock = RTC.now();
    
   if (CheckDST(clock))
   {
      hourval = (clock.hour() + 1) % 24;
   }
   else
    hourval = clock.hour(); 

    minuteval = clock.minute(); 
    secondval = clock.second();
    matrix.writeDigitNum(0, hourval / 10);
    matrix.writeDigitNum(1, hourval % 10);
    matrix.writeDigitNum(3, minuteval / 10);
    matrix.writeDigitNum(4, minuteval % 10);
    bool isEven = false;
    
    if ((secondval % 2) == 0)
    {
      isEven = true;
    }
    
    matrix.drawColon(isEven);
    matrix.writeDisplay();
  }
  else if (!dimmerActive)
  {
    //Serial.println("FACEOFF");
    matrix.clear();
    matrix.writeDisplay(); 
  }
    
   //if it's alarm time
   if (mode != ALARM)
   {
     if (hourval==6 && minuteval ==15) //not the best way to do this
     {
       //sunrise(); //TODO: fix this shit.  it hangs sometimes after 1 min
     }
   }
  
   //LED functions
   val1 = analogRead(SENSOR_PIN_IRD);
   delay(DEBOUNCE_DELAY);
   val2 = analogRead(SENSOR_PIN_IRD);
   delay(DEBOUNCE_DELAY);
   val3 = analogRead(SENSOR_PIN_IRD);
   delay(DEBOUNCE_DELAY);
   val4 = analogRead(SENSOR_PIN_IRD);
   int avgRead =  constrain(((val1 + val2 + val3 + val4) >> 2), 249, 600);
   
   readingPWM =  map(avgRead, 250, 600, 256, 0);
   int diff = abs(readingPWM - lastPWM);
   lastPWM = readingPWM;
   
   if (diff > smoothPWM)
   {
     return;
   }
  
  if (readingPWM > 50 && readingPWM < 256)
  {
    switch(mode)
    {
      case INACTIVE:
        if (readingPWM > 0) //TODO: huh?
        {
          mode=LAMP;
          Serial.println(readingPWM, DEC);
          fadeInWhite(254,3);
          delay(500);
        }
        break;
      
      case LAMP:
        Serial.print("Read PWM: ");
        Serial.println(readingPWM);
        int newval, mapPercent;
        
        if (readingPWM > 60 && readingPWM < 255)
          dimmerActive = true;
          
        if (readingPWM > 60 && readingPWM < 150) //lowering
        {
          newval = currentLightPWM - DIMMER_STEP;
        }
        else if (readingPWM >150 && readingPWM < 255) //raising
        {
          newval = currentLightPWM + DIMMER_STEP;
        }
        
        if (newval > 255)
          newval = 255;
          
        if (newval < 0)
          newval = 0;

        currentLightPWM = newval;

        Serial.print("Light PWM: ");
        Serial.println(currentLightPWM);
        mapPercent = map(currentLightPWM, 0, 255, 1, 100);
        
        matrix.drawColon(false);
        matrix.println(mapPercent, DEC);
        matrix.writeDisplay();
        
        setColors(currentLightPWM, currentLightPWM, currentLightPWM);
        break;
      
      case ALERT:
      case ALARM:
      if (readingPWM > 1)
        {
          setOff();
        }
        break;
    } 
  }
  else if (readingPWM > 1 && readingPWM < 50)
  {
    Serial.println("off");
    setOff();
  }
  else
  {
    delay(200);
    dimmerActive = false;
  }
}

//LIGHT STUFF

void startUp()
{
  //for(int i=0;i<300;i++){
      //setColors(redList[i], greenList[i], blueList[i]);
      //delay(10);
    //}
    delay(2000);
    //== 5 seconds  long enough for the PIR to warm up and calibrate
}

void setColors(int r, int g, int b)
{
  for(int i=0;i<NUMPIXELS;i++){
    pixels.setPixelColor(i, pixels.Color(r,g,b));
    pixels.show();
  }
}

void fadeInWhite(int level, int stepDelay)
{
  for(int s=level;s>0;s--){
    setColors(level - s,level-s,level-s);
    delay(stepDelay);
    currentLightPWM = level;
  }
}

void fadeOut(int stepDelay)
{
  for(int s=0;s< currentLightPWM + 1;s++){
    setColors(currentLightPWM - s,currentLightPWM-s,currentLightPWM-s);
    delay(stepDelay);
     
  }
  currentLightPWM = 0;
}

void setOff()
{
  if (tickEvent >= 0)
  {
    t.stop(tickEvent);
    tickEvent = -1;
  }
  sunrise_index = 0;
  fadeOut(2);
//  setColors(0,0,0);
  mode = INACTIVE;
  delay(1000);
}

void sunrise()
{
  mode = ALARM;
  tickEvent = t.every(SUNRISE_DELAY, sunrise_tick, SUNRISE_TICK_COUNT);
}

void sunrise_tick()
{
  //setColors(redList[sunrise_index], greenList[sunrise_index], blueList[sunrise_index]);
  sunrise_index++;
}

bool CheckDST(DateTime tClock)
{
  
//  Daylight saving time starts on the second Sunday in March and ends on the first
//  Sunday in November, with the time changes taking place at 2:00 a.m. local time.
// With a mnemonic word play referring to seasons, clocks "spring forward and fall back"—that
// is, in spring (technically late winter) the clocks are moved forward from 2:00
// a.m. to 3:00 a.m., and in fall they are moved back from 2:00 am to 1:00 am.
  
        if (tClock.month() < 3 || tClock.month() > 11) { return false; }
        //April to October are in
        if (tClock.month() > 3 && tClock.month() < 11) { return true; }
        int previousSunday = tClock.day() - tClock.dayOfWeek();
        //In march, we are DST if our previous sunday was on or after the 8th.
        if (tClock.month() == 3) { return previousSunday >= 8; }
        //In november we must be before the first sunday to be dst.
        //That means the previous sunday must be before the 1st.
        return previousSunday <= 0;
}




full of sexy sunrise alarm data (not implemented) and arduino timing cheats.

Discussions