I mentioned the reasoning behind this project in a previous post, please read it if you can't figure out why anyone would ever want to do this.
Basically, the idea is to have an arduino controlled room light. The primary goal is to be able to turn the light on and off based on a fixed time table from the moment the light switch is flipped. A second goal is to fit the components for the build in the same space the light switch would have occupied (basically one side of a dual-gang box).
The first thing you will realize, given the second requirement above, is that a standard arduino will not fit in the space available. At the time I was planning this, there were several mini arduino compatibles which were a possible fit. But then I found this article on using a BlinkM MinM as a tiny arduino. Given the simple goals of this project I should only need a single output pin to control a relay. The MinM gives me 2 I/O pins and an RGB LED to play with. Just what the doctor ordered.
I found myself with an extra pin that I wasn't using so I decided to extend the functionality by allowing a visitor to disable the timer. I toyed with the idea of putting a button on this I/O pin and maybe making the user click in a specific sequence to disable the timer but I quickly discarded the idea. What else can be done with a single pin? That's when I remembered the 1-wire family of devices by Maxim Integrated (formerly Dallas Semiconductor). I decided to use my last pin for a 1-wire iButton reader.
Components I ended up using were:
- BlinkM MinM
- iButton Touch Probe with LED (DS9092L)
- Any iButton
- Dual Decora faceplate
- CATV Decora insert
- Right-angle headers
- Seeedstudio Electronic brick -5V Relay module
- Mini USB power supply
The idea is that the circuit is not even powered until the light switch is turned on. Once the switch is turned on, it supplies power to the USB power adapter which powers the MinM and the green LED on the iButton reader. When the MinM powers up it sets pin D to high turning on the relay and the light that's attached to it. The arduino sketch takes care of turning the relay/light on and off according to a schedule, driving the RGB LED built into the MinM and reading the iButton serial number if present (every iButton has a unique serial number).
I removed the CATV connecter from the Decora filler and made the hole larger until I could fit the iButton reader in its place. I also glued an angle header to the Decora faceplate so that I could attach and remove the MinM as needed to reprogram it (the MinM has press-fit header holes which keep it securely attached without soldering). A small hole is drilled in the faceplate that lines up with the LED on the MinM allowing it to shine through.
Decora insert from the back
Decora insert from the front
Everything that will be stuffed in the gang box
After the hardware was sorted out, I wrote an arduino sketch to bring everything together. Using the OneWire library made everything pretty straight forward. Since there is no security requirement, the code doesn't actually check the iButton serial number, just the fact that an iButton was connected.
I actually spent much more time playing with the built in RGB LED. The ATtiny85 that powers the MinM does not support PWM on all of the rgb pins so I wrote a function that fades the LEDs from one color to another in a specified time period. I use it to provide a nice fade-in/fade-out effect.
Below is the code that is currently running on the light. Although I program for a living, this is my first Arduino sketch.
I actually spent much more time playing with the built in RGB LED. The ATtiny85 that powers the MinM does not support PWM on all of the rgb pins so I wrote a function that fades the LEDs from one color to another in a specified time period. I use it to provide a nice fade-in/fade-out effect.
Below is the code that is currently running on the light. Although I program for a living, this is my first Arduino sketch.
#include <OneWire.h>
const int buttonPin = 2;
const int relayPin = 0;
const int redPin = 3;
const int grnPin = 4;
const int bluPin = 1;
const int pollInterval = 100;
const unsigned int minuteMillis = 60000;
const int startReminders = 15; // minutes until we start flashing reminders
unsigned int elapsedMinutes = 0; // minutes since start of timing
unsigned int elapsedMillis = 0; // milliseconds of current second
int reminderElapsed = -1; // milliseconds since this reminder started
// Serial iButton
OneWire ds(buttonPin); // on pin 10
byte addr[8];
byte infiniteLives = 0;
// # of items in the reminderDuration array
byte reminderCount = 10;
// When reminders start, we will switch light off for given number of milliseconds
int reminderDuration[] = { 2000, 2000, 2000, 2000, 2000, 3000, 3000, 3000, 3000, 3000 };
void setup() {
pinMode(relayPin, OUTPUT);
pinMode(redPin, OUTPUT);
pinMode(grnPin, OUTPUT);
pinMode(bluPin, OUTPUT);
pinMode(buttonPin, INPUT);
// Switch light ON when powered up
digitalWrite(relayPin, HIGH);
}
void loop() {
delay(pollInterval);
elapsedMillis = elapsedMillis + pollInterval;
if(infiniteLives == 1) {
// iButton key previously verified, slow blink
rgbTransition(0, 0, 0, 0, 0, 99, 500); // fade blue on
rgbTransition(0, 0, 99, 0, 0, 0, 500); // fade blue off
delay(5000);
return;
}
// check if iButton is present
if ( !ds.search(addr)) {
ds.reset_search();
} else if ( OneWire::crc8( addr, 7) != addr[7]) {
rgbTransition(0, 0, 0, 99, 0, 99, 50); // fade purple on
rgbTransition(99, 0, 99, 0, 0, 0, 50); // fade purple off
rgbTransition(0, 0, 0, 99, 0, 99, 50); // fade purple on
rgbTransition(99, 0, 99, 0, 0, 0, 50); // fade purple off
} else {
// Acknowledge valid iButton
rgbTransition(0, 0, 0, 0, 99, 0, 50); // fade green on
rgbTransition(0, 99, 0, 0, 0, 0, 50); // fade green off
rgbTransition(0, 0, 0, 0, 99, 0, 50); // fade green on
rgbTransition(0, 99, 0, 0, 0, 0, 50); // fade green off
// Turn on lights (in case they were off)
digitalWrite(relayPin, HIGH);
infiniteLives = 1;
ds.reset();
return;
}
if(reminderElapsed >= 0)
reminderElapsed = reminderElapsed + pollInterval;
elapsedMillis = elapsedMillis % minuteMillis;
// Another minute has passed
if(elapsedMillis == 0) {
elapsedMinutes++;
if(elapsedMinutes >= startReminders) {
if(elapsedMinutes >= startReminders+reminderCount) {
// Leave light off
digitalWrite(relayPin, LOW);
// "Evil" blink (red)
rgbTransition(0, 0, 0, 75, 0, 0, 500);
rgbTransition(75, 0, 0, 0, 0, 0, 500);
} else {
if(reminderDuration[elapsedMinutes - startReminders] > 0) {
// start the reminder timer
reminderElapsed = 0;
digitalWrite(relayPin, LOW);
}
// "Danger" blink
rgbTransition(0, 0, 0, 75, 75, 0, 500); // fade red+green on
rgbTransition(75, 75, 0, 0, 0, 0, 500); // fade red+green off
}
} else {
// "All's Well" blink
rgbTransition(0, 0, 0, 0, 75, 0, 250); // fade green on
rgbTransition(0, 75, 0, 0, 75, 75, 250); // fade blue on
rgbTransition(0, 75, 75, 0, 0, 75, 250); // fade green off
rgbTransition(0, 0, 75, 0, 0, 0, 250); // fade blue off
}
}
if(reminderElapsed >= 0) {
if(reminderElapsed == reminderDuration[elapsedMinutes - startReminders])
{
// disable the reminder timer
reminderElapsed = -1;
digitalWrite(relayPin, HIGH);
}
}
}
// PWM transition from rgb1 -> rgb2 in 'duration' milliseconds
// rgb values must be 0-99
void rgbTransition(char r1, char g1, char b1, char r2, char g2, char b2, int duration)
{
for(int step = 0;step < duration;step++) {
// Each millisecond is a step in the transition
char rOn = r1+(r2-r1)*float(step)/float(duration);
char gOn = g1+(g2-g1)*float(step)/float(duration);
char bOn = b1+(b2-b1)*float(step)/float(duration);
for(byte i=0;i<3;i++) {
if(rOn>0)
digitalWrite(redPin, HIGH);
if(gOn>0)
digitalWrite(grnPin, HIGH);
if(bOn>0)
digitalWrite(bluPin, HIGH);
byte j = 0;
while(j<100) {
if(rOn == j)
digitalWrite(redPin, LOW);
if(gOn == j)
digitalWrite(grnPin, LOW);
if(bOn == j)
digitalWrite(bluPin, LOW);
j = j + 1;
}
}
}
digitalWrite(redPin, LOW);
digitalWrite(grnPin, LOW);
digitalWrite(bluPin, LOW);
}
No comments:
Post a Comment