Skip to content

Instantly share code, notes, and snippets.

@platisd
Last active December 30, 2016 11:01
Show Gist options
  • Save platisd/dce6760eb1aaaf2c6ff093c24352cb40 to your computer and use it in GitHub Desktop.
Save platisd/dce6760eb1aaaf2c6ff093c24352cb40 to your computer and use it in GitHub Desktop.
Pillbox docs

Pillbox flowchart

An abstract overview of the system's operation (some details missing)

Flowchart

Pillbox schematic

Schematic

Pillbox breadboard view

Breadboard

#include <RTClib.h>
#include <SoftwareSerial.h>
RTC_DS1307 rtc;
SoftwareSerial bluetooth(10, 11); // RX, TX
enum BoxState {
TIME_NOT_SET, // We are in this state when starting and no time has been set
TIME_SET_NOW_WAITING, // This is the normal operating state. Time is set and waiting to ring
RINGING // Ringing
};
struct Alarm {
int hour;
int minute;
};
const int numOfLids = 2; // How many lids we have
BoxState currentState[numOfLids] = {TIME_NOT_SET, TIME_NOT_SET};
BoxState prevState[numOfLids] = {TIME_NOT_SET, TIME_NOT_SET};
const int MAG_SWITCH[numOfLids] = {2, 3}; // Pins for magnetic switches for lid 1 and 2
const boolean LID_OPEN = HIGH;
const boolean LID_CLOSED = LOW;
const int LED[numOfLids] = {4, 5}; // Led pins for lid 1 and 2
const int BUZZER = 9;
boolean buzzerState [numOfLids] = {false, false};
boolean alreadyRung [numOfLids] = {false, false};
Alarm lid[numOfLids] = {{25, 61}, {25, 61}}; // Some initial bad values
unsigned long blinkFreq[numOfLids] = {0, 0}; // 0 indicates that led should be off, 1 constantly on and anything else is a frequency
unsigned long prevBlink[numOfLids] = {0, 0}; // The moment that the led was last blinked
boolean lidForgottenOpen[numOfLids] = {false, false};
boolean ledState[numOfLids] = {false, false};
boolean setAlarm[numOfLids] = {false, false};
void setup() {
Serial.begin(9600);
bluetooth.begin(9600);
for (int i = 0; i < numOfLids; i++) {
pinMode(LED[i], OUTPUT);
pinMode(MAG_SWITCH[i], INPUT);
digitalWrite(MAG_SWITCH[i], HIGH); // Set the internal pullups
}
pinMode(BUZZER, OUTPUT);
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1); //block the execution
}
// Set the time for the first time if the clock is NOT running
if (!rtc.isrunning()) {
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
} else {
DateTime now = rtc.now();
Serial.print("Current time: ");
Serial.print(now.hour());
Serial.print(":");
Serial.println(now.minute());
}
}
void loop() {
if (bluetooth.available()) {
// We are expecting a string in the form of Hour:Minute,Hour:Minute*
// * is the package delimiter
String input = bluetooth.readStringUntil('*');
String lidInput[2];
lidInput[0] = input.substring(0, input.indexOf(","));
lidInput[1] = input.substring(input.indexOf(",") + 1);
for (int i = 0; i < numOfLids; i++) {
lid[i].hour = lidInput[i].substring(0, lidInput[i].indexOf(":")).toInt();
lid[i].minute = lidInput[i].substring(lidInput[i].indexOf(":") + 1).toInt();
setAlarm[i] = lid[i].hour <= 24; // if time is set correctly it will be a valid hour
}
}
for (int i = 0; i < numOfLids; i++) {
switch (currentState[i]) {
case TIME_NOT_SET:
if (isAlarmSet(i)) {
setState(i, TIME_SET_NOW_WAITING);
blinkFreq[i] = 0; // Keep the led continuously OFF
} else {
blinkFreq[i] = 1; // Keep the led continuously ON
}
break;
case TIME_SET_NOW_WAITING:
if (isLidOpen(i)) { // We should not have an open lid in this state
blinkFreq[i] = 1000; // Blink the led every 1 sec to indicate that lid should close
} else {
lidForgottenOpen[i] = false; // Indicate that lid is not (forgotten) open
setBuzzer(i, LOW); // Make sure buzzer not ringing if lid is closed (and kept ringing due to a lid forgotten open)
blinkFreq[i] = 0; // The LED shouldn't be blinking if the lid is shut (unless it's time to open which is determined later)
}
if (isTimeToOpen(i) && !alreadyRung[i]) { // Ring only if it is time and we have not already rung in the same minute
blinkFreq[i] = 300; // Blink the led 3 times per sec to indicate that lid should open
setBuzzer(i, HIGH); // Start ringing the buzzer
setState(i, RINGING); // Set state to RINGING
alreadyRung[i] = true; // So to make sure we don't ring again during the same minute if the user shuts the lid
if (isLidOpen(i)) { // If it is time to open and the lid is already open, then it's probably forgotten
lidForgottenOpen[i] = true;
}
} else { // If it isn't time to open and the lid is closed then the led shouldn't be blinking
if (!isTimeToOpen(i)) {
alreadyRung[i] = false; // If it is not time to ring, then we definitely have not already rung
}
}
break;
case RINGING:
if (isLidOpen(i)) { // If lid is open then we should stop ringing
if (!lidForgottenOpen[i]) {
setBuzzer(i, LOW); // Stop ringing the buzzer only if the lid wasn't forgotten open
}
setState(i, TIME_SET_NOW_WAITING); // Go back to previous state
}
break;
default: // We should never be here
break;
}
}
blinkLeds();
}
/**
lidID: The relevant lid
newState: The state that the related lid wants to set the buzzer
Tries to turn buzzer ON or OFF but only if both lids agree on the state
*/
void setBuzzer(int lidID, boolean newState) {
buzzerState[lidID] = newState;
boolean result = false;
// The state of the buzzer is determined by the combination of the states
// that the different lids want to set it. If one lid wants to set it HIGH
// then it should be able to. But if a lid wants to set the buzzer LOW then
// it should only go low if all of the lids agree on it. This is done in case
// a lid starts the buzzer, then the other lid starts the buzzer as well, but
// the buzzer should be turned off when BOTH of the lids are shut.
for (int i = 0; i < numOfLids; i++) {
result = result || buzzerState[i];
}
digitalWrite(BUZZER, result);
}
/**
Blink the leds according to the frequency set in the blinkFreq array
*/
void blinkLeds() {
for (int i = 0; i < numOfLids; i++) {
if (blinkFreq[i] > 1) { // the led should blink at the specified frequency
unsigned long currentTime = millis();
if (currentTime >= prevBlink[i] + blinkFreq[i]) {
prevBlink[i] = currentTime;
setLed(i, !ledState[i]); // Set the led to the opposite state than it currently is
}
} else { // Led should be off if the freq is 0 or on if freq is 1
setLed(i, (blinkFreq[i] == 1));
}
}
}
/**
@param lidID: The related LED (the lidID matches the related lid)
@param newState: The new state for the LED
Set the specifiied led to the supplied state and log down the current state
*/
void setLed(int lidID, boolean newState) {
ledState[lidID] = newState; // Save the current led state
digitalWrite(LED[lidID], newState);
}
/**
@param lidID: The related lid
@param newState: The new state for the lid to be set
Saves the old state and sets the new
*/
void setState(int lidID, BoxState newState) {
prevState[lidID] = currentState[lidID];
currentState[lidID] = newState;
}
/**
@param lidID: The related lid
@return Whether the alarm for the related lid has been set
Checks if the alarm for the specified lid is set
*/
boolean isAlarmSet(int lidID) {
return setAlarm[lidID];
}
/**
@param lidID: The related lid
@return Whether the specified lid is open
Checks if the specified lid is open
*/
boolean isLidOpen(int lidID) {
return digitalRead(MAG_SWITCH[lidID]) == LID_OPEN;
}
/**
@param lidID: The related lid
@return Wether it is time to open the related lid
Checks if it is time for the user to open the specified lid
*/
boolean isTimeToOpen(int lidID) {
DateTime now = rtc.now();
return now.hour() == lid[lidID].hour && now.minute() == lid[lidID].minute;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment