A WYM Product
"What if you didn't use your desk? What if it used you?"
Through personification, D.A.D. is an antagonistic— yet caring— device for those who aren't quite "grown up" but aren't quite children. It's meant to simulate the annoying homely, yet also distant care of a "dad". It's not a replacement for a parent, but it provides the presence of one.
So why D.A.D?
Have you ever turned on your lights and then accidentally fallen asleep? D.A.D is here to prevent that! D.A.D monitors if you’ve turned on your lamp and have fallen asleep, then does a suitable action based on your offenses. First few times, he’ll shut the lamp off for you. No biggie, kiddo. But... the next times, you might be getting woken up by the flashy lights and tones. Don’t worry though, D.A.D will not hold old habits against you, and his offense counter will reset after some time!
Explore the World of D.A.D.
Behind the D.A.D.
We love and appreciate D.A.D., but there is quite a lot that went into allowing him to fulfill his dadly duties.
SLA
What Does D.A.D Sense?
- The Time - Using the RTC module, we detect the time and activate the sequence when it’s bedtime.
- Your Motion - Using the PIR sensor, we sense if there is motion for a certain period of time. If there isn’t, we assume you’re asleep!
- Light - Using a photosensor, we know if you leave that dang light on!
- Activates LEDs
- Activates speaker
- Shuts lamp with slight disappointment
- Changes his LCD display depending on his mood
Construction
The D.A.D. is composed of two Arduinos that communicate to each other using IR signalling/receiving.
List of Parts
- 2 Arduino Unos
- 1 16x2 LCD
- 1 Piezo Buzzer
- 1 60 count NeoPixel LED strip
- 1 Photoresistor
- 1 IR Reciever
- 1 5V Relay
- 2 10K ohm resistors
- 1 220 ohm resistor
- 1 10K ohm potentiometer
- 1 RTC
- 1 PIR Sensor
- 1 IR LED
- 1 Button
- Lots of Wires
- 3 breadboards
The D.A.D. Bod
The Companion Bit
Code
You will need the following libraries:
- Adafruit.NeoPixel
- IRRemote
- LiquidCrystal
A note on the code:
At times, the code may seem unnecessarily redundant. This is in fact, intentional. Due to timing conflicts from running so many libraries at what wants to be the same time, a work around was required, resulting in a system that runs many parts on a stop/start basis. Also basic functions, like while statements, would commonly screw up the triggering of start/stops, so are instead replaced by a series of if statements that do the same thing—though less elegantly.
The D.A.D. Bod
#define DECODE_NEC
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
#include <IRremote.hpp>
#include <LiquidCrystal.h>
#define LED_PIN 6
#define LED_COUNT 60
#define BRIGHTNESS 255// Set BRIGHTNESS to about 1/5 (max = 255)
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRBW + NEO_KHZ800);
int numRound = 0;
bool lightLevel = false;
bool alarmSound = false;
bool motionPIR = false;
bool offSwitch = false;
bool pleaseWork = false;
LiquidCrystal lcd(12,11,5,4,3,1);
int h=12;
int m;
int s;
int flag;
int TIME;
const int hs=8;
const int ms=9;
int state1;
int state2;
int days = 0;
int offense = 6;
int relay = A1;
volatile byte relayState = LOW;
void setup(){
IrReceiver.begin(2, ENABLE_LED_FEEDBACK);
lcd.begin(16,2);
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(BRIGHTNESS);
pinMode(relay, OUTPUT);
digitalWrite(relay, HIGH);
Serial.begin(9600);
}
void loop(){
clock();
if(flag >= 22 || flag <= 6 ){
lighted();
}
if (lightLevel == true){
recieved();
if (numRound == 30){
pleaseWork = true;
alarmSound = false;
motionPIR = false;
}
if (pleaseWork == true){
digitalWrite(relay, LOW);
offense++;
days = 0;
}
}
offenseCount();
if (flag == 6){
digitalWrite(relay, HIGH);
}
}
void pulseWhite(uint8_t wait) {
for(int j=0; j<255; j++) { // Ramp up from 0 to 255
// Fill entire strip with white at gamma-corrected brightness level 'j':
strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
strip.show();
delay(wait);
}
for(int j=255; j>=0; j--) { // Ramp down from 255 to 0
strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
strip.show();
delay(wait);
}
}
void clock() {
lcd.setCursor(0,0);
s=s+1;
lcd.print("TIME:" );
lcd.print(h);
lcd.print(":");
lcd.print(m);
lcd.print(":");
lcd.print(s);
if(flag<12) lcd.print(" AM");
if(flag==12) lcd.print(" PM");
if(flag>12) lcd.print(" PM");
if(flag==24) flag=0;
delay(1000);
lcd.clear();
if(s==60) {
s=0;
m=m+1;
}
if(m==60) {
m=0;
h=h+1;
flag=flag+1;
}
if(h==13) {
h=1;
}
lcd.setCursor(0,1);
//-----------Time setting----------//
state1=digitalRead(hs);
if(state1==1) {
h=h+1;
flag=flag+1;
if(flag<12) lcd.print(" AM");
if(flag==12) lcd.print(" PM");
if(flag>12) lcd.print(" PM");
if(flag==24) flag=0;
if(h==13) h=1;
}
state2=digitalRead(ms);
if(state2==1) {
s=0;
m=m+1;
}
}
void recieved() {
long val = 0;
if (IrReceiver.decode()) {
IrReceiver.printIRResultShort(&Serial); // optional use new print version
IrReceiver.resume();
val = IrReceiver.decodedIRData.decodedRawData;
}
delay(100);
val = abs(val);
if (val == 417792256){
motionPIR = true;
}
if(val == 217252096 ){
alarmSound = true;
}
if (val == 384368896){
offSwitch = true;
}
if(offSwitch == true){
pleaseWork = true;
alarmSound = false;
motionPIR = false;
offSwitch = false;
}
if(alarmSound == true && motionPIR == true){
alarmSystem();
numRound++;
}
}
void offenseCount() {
if(flag == 6){
days++;
}
if (days >= 180){
days = 0;
offense = 0;
}
}
void lighted(){
int analogValue = analogRead(A0);
//Serial.println(analogValue); // the raw analog reading
if (analogValue <= 31){
Serial.println(" - Light Present");
lightLevel = true;
} else if (analogValue > 1000){
Serial.println(" - Dark");
lightLevel = false;
}
delay(100);
}
void alarmSystem(){
if(offense <= 2){
pleaseWork = true;
alarmSound = false;
motionPIR = false;
} else if(offense >= 3 && offense <= 5) { //alarm only
IrReceiver.stop();
tone(8,3729);
delay(500);
tone(8,3520);
delay(700);
noTone(8);
IrReceiver.start();
} else { // alarm and lights
IrReceiver.stop();
tone(8,3729);
delay(500);
tone(8,3520);
delay(700);
noTone(8);
pulseWhite(0.1);
delay(100);
IrReceiver.start();
}
}
The Companion Bit
void setup() {
#include <IRremote.hpp>
int button = 12;
char buttonState = LOW;
// the pin that the LED is atteched to
int sensor = 2; // the pin that the sensor is atteched to
int state = LOW; // by default, no motion detected
int val = 0;
void setup() {
pinMode(sensor, INPUT); // initialize sensor as an input
pinMode(button, INPUT);
IrSender.begin(3, ENABLE_LED_FEEDBACK);
Serial.begin(9600);
}
void loop() {
val = digitalRead(sensor); // read sensor value
if (val == HIGH) { // check if the sensor is HIGH
digitalWrite(state, HIGH); // turn LED ON
delay(500); // delay 100 milliseconds
if (state == LOW) {
Serial.println("Motion detected!");
state = HIGH;
IrSender.sendNEC(0x0, 0x18, 2);
delay(40); // update variable state to HIGH
}
} else {
digitalWrite(button, LOW); // turn LED OFF
delay(500); // delay 200 milliseconds
if (state == HIGH){
Serial.println("Motion stopped!");
state = LOW;
IrSender.sendNEC(0x0, 0xC, 2);
delay(40); // update variable state to LOW
}
}
buttonState = digitalRead(button);
//Serial.println(buttonState);
if(buttonState== HIGH){
Serial.println("yes");
for(int i=0; i<3; i++){
IrSender.sendNEC(0x0, 0x16, 2);
delay(40);
}
delay(1000);
}
}
In Summary / A Note on IR sending
If you've made it this far—congrats—I know that was a lot. Hopefully this can serve as some reference, if you endeavour to embark upon a similar maker journey. I would last like to end this off with one last reference/tip that will hopefully help (though really not enough) in your progress. If you attempt to send an IR Signal, don't bother looking for tutorials online— there aren't any that are worth the hours you will waist attempting to find nothing. Instead just refer to the notes on the library as listed in the github page for the IR Library (linked here). As a result of widespread naming changes to functionality across the many versions, most tutorials online use out-off-date components. From library, use the ReceiveDemo example with a remote of your choosing to determine your codes. Then use some basic layout like this for actual receiving in code for product:
#include <IRremote.hpp>
void setup(){
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); // Start the receiver
}
void loop() {
if (IrReceiver.decode()) {
Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX);
//IrReceiver.printIRResultShort(&Serial); // optional use new print version
IrReceiver.resume(); // Enable receiving of the next value
}
}
(This section of code can also be found in the link above)
As for Sending use:
#include <IRremote.hpp>
void setup() {
Serial.begin(9600);
IrSender.begin(/*pin number*/, ENABLE_LED_FEEDBACK);
}
void loop() {
IrSender.send/*protocol type*/(/* Address(ex. 0x0), Command(ex. 0x16), # ofRepeats */);
delay(40);
}
(Address and Command are both be found using the ReceiveDemo. Protocol Type is the remote protocol type you are using.)
With this information you should be much better prepped to tackle the wide world of IR communication. I will also note that I'm writing this as of version 4.0, in Dec 2022. Later updates may result in further changes to the library, so this could become yet another irrelevant set of instructions.
That's all from us. Happy making!
Created by Nat Musenga, Emily White, and Janey Yee
Physical Computing: Autumn 2022