diff options
| author | Syndamia <kamen.d.mladenov@protonmail.com> | 2020-07-08 14:15:23 +0300 |
|---|---|---|
| committer | Syndamia <kamen.d.mladenov@protonmail.com> | 2020-07-08 14:15:23 +0300 |
| commit | 1540a8deb612a9f5c3895aca13ff94e55cceb14e (patch) | |
| tree | cc659ad32a89e13a6562a66515a11890491a6118 | |
| parent | 84fc8ea0832eecfa8b82d5ed070aa72de958c0b2 (diff) | |
| download | RemiHap-1540a8deb612a9f5c3895aca13ff94e55cceb14e.tar RemiHap-1540a8deb612a9f5c3895aca13ff94e55cceb14e.tar.gz RemiHap-1540a8deb612a9f5c3895aca13ff94e55cceb14e.zip | |
Added first versions of final arduino code files.
| -rw-r--r-- | remihap/alarm.ino | 100 | ||||
| -rw-r--r-- | remihap/dispenser.ino | 45 | ||||
| -rw-r--r-- | remihap/remihap.ino | 444 |
3 files changed, 589 insertions, 0 deletions
diff --git a/remihap/alarm.ino b/remihap/alarm.ino new file mode 100644 index 0000000..d754331 --- /dev/null +++ b/remihap/alarm.ino @@ -0,0 +1,100 @@ +#include <Adafruit_NeoPixel.h> + +#define NOTE_C5 523 +#define NOTE_D5 587 +#define NOTE_E5 659 +//Defining the notes' frequencies for the melody + +#define interval 10000 +//Time in millisecconds between first and second alarm + +#define SIGNAL_PIN 3 +#define STRIP_PIN 9 +#define LED_COUNT 4 +#define PIEZZO_PIN 10 + +#define MELODY_LENGTH 4 + +int melodies[2][MELODY_LENGTH] = +{ + {NOTE_C5, NOTE_E5, NOTE_D5, 0}, + {NOTE_C5, NOTE_C5, NOTE_C5, NOTE_C5} +}; + +int noteDurations[] = +{ + 1000 / 8, 1000 / 8, 1000 / 8, 1000 / 2 +}; + +Adafruit_NeoPixel strip = Adafruit_NeoPixel(LED_COUNT, STRIP_PIN, NEO_GRB + NEO_KHZ800); +unsigned long color = strip.Color(255, 255, 255); + +void ledToggle() +{ + if(strip.getPixelColor(0) == strip.Color(0,0,0)) + { + strip.fill(color); + } + else + { + strip.clear(); + } + strip.show(); +} + +void playNote(int noteDuration, int noteToPlay) +{ + tone(PIEZZO_PIN, noteToPlay, noteDuration); + delay(noteDuration * 1.30); + + noTone(PIEZZO_PIN); +} + +void playMelodyAndLEDs(int position) +{ + for(int i = 0; i < MELODY_LENGTH; i++) + { + playNote(noteDurations[i], melodies[position][i]); + ledToggle(); + delay(100); + + } +} + +void setup() +{ + pinMode(SIGNAL_PIN, INPUT); + pinMode(STRIP_PIN, OUTPUT); + pinMode(PIEZZO_PIN, OUTPUT); + strip.begin(); +} + +void alarm(unsigned long timeSinceActivation) +{ + while(digitalRead(SIGNAL_PIN)) + { + if(millis() - timeSinceActivation > interval) + { + color = strip.Color(255, 0, 0); + playMelodyAndLEDs(1); + } + else + { + color = strip.Color(255, 255, 0); + playMelodyAndLEDs(0); + } + } +} + + + +void loop() +{ + if(digitalRead(SIGNAL_PIN)) + { + alarm(millis()); + } + delay(100); +} + + diff --git a/remihap/dispenser.ino b/remihap/dispenser.ino new file mode 100644 index 0000000..f3a5999 --- /dev/null +++ b/remihap/dispenser.ino @@ -0,0 +1,45 @@ +#include <Servo.h> +// Note: servo.h library disables analogWrite on pins 9 and 10 + +#define INPUT_PIN A2 + +Servo servos[5]; +const int SERVO_PINS[] = {5, 6, 9, 10, 11}; + +int i; // variable for loops + +void zeroOutServos() +{ + for (i = 0; i < sizeof(servos)/sizeof(*servos); i++) // would've used range-based for loop, but its not supported on C++98 + { + servos[i].write(0); + } +} + +void setup() { + pinMode(INPUT_PIN, INPUT); + + for(i = 0; i < sizeof(servos)/sizeof(*servos); i++) + { + pinMode(SERVO_PINS[i], OUTPUT); + servos[i].attach(SERVO_PINS[i]); + } + + delay(1500); + zeroOutServos(); +} + +void dispensePill(int servoNumber) +{ + if (servoNumber > 0) { + servos[servoNumber - 1].write((servos[servoNumber - 1].read() == 180)?0:180); + delay(1500); + } +} + +void loop() +{ + dispensePill(pulseIn(INPUT_PIN, HIGH) / 1000); + + delay(100); +} diff --git a/remihap/remihap.ino b/remihap/remihap.ino new file mode 100644 index 0000000..ad12923 --- /dev/null +++ b/remihap/remihap.ino @@ -0,0 +1,444 @@ +#include <Keypad.h> +#include <LiquidCrystal.h> +#include <Adafruit_NeoPixel.h> + +// LCD + +#define LCD_ROWS 2 +#define LCD_COLS 16 + +#define MSG_ERR_R1 " Error! " +#define ERROR_VISIBILITY_TIMEOUT 2000 + +#define MSG_ERR_DAY " Invalid day " +#define MSG_ERR_HOUR " Invalid hour " +#define MSG_ERR_MIN "Invalid minutes " +#define MSG_ERR_DIS "Invalid dispensr" + +/* Alarm menu */ + +#define MSG_NO_ALRM_R1 " No alarms " +#define MSG_NO_ALRM_R2 " #: Create new " + +#define MSG_EDIT_TOOLS "# A B C D " +#define MSG_HOUR " Hour(0-23) #" +#define MSG_MINUTES " Minutes(0-59) #" +#define MSG_DISPENSER " Dispenser #" +#define MSG_CONFIRM "Sure? Yes:# No:C" + +/* Week menu */ + +#define MSG_DAY_NUM "Day number(1-7) " +#define MSG_WEEK_TOGGLE " A - toggle " + +/* Main menu */ + +#define MSG_MAIN_R1 " A: Week days " +#define MSG_MAIN_R2 " B: Alarms " + +// Keypad + +#define KEYPAD_ROWS 4 +#define KEYPAD_COLS 4 + +// Alarms + +#define ALARMS_PER_DAY 10 +#define WEEK_LENGTH 7 + +//Defining the notes' frequencies for the melody +#define NOTE_C5 523 +#define NOTE_D5 587 +#define NOTE_E5 659 + +//Time in millisecconds between first and second alarm +#define interval 10000 + +#define ALARM_PIN 11 +#define BTN_PIN 12 +#define DISPENSER_PIN 10 + +int i, j; //variables for loops + +/* + * --------------------------------------------- +*/ + +// +// LCD +// + +LiquidCrystal lcd(A0, A1, A2, A3, A4, A5); + +void printToLCD(int rowID, char message[]) +{ + lcd.setCursor(0, rowID); + + for(i = 0; i < LCD_COLS; i++) + { + lcd.write(message[i]); + delay(1); //TinkerCad optimization + } +} + +void printErrorToLCD(char message[]) +{ + printToLCD(0, MSG_ERR_R1); + printToLCD(1, message); + + delay(ERROR_VISIBILITY_TIMEOUT); +} + +// +// Keypad +// + +char keys[KEYPAD_ROWS][KEYPAD_COLS] = +{ + {'1','2','3','A'}, + {'4','5','6','B'}, + {'7','8','9','C'}, + {'*','0','#','D'} +}; +byte rowPins[KEYPAD_ROWS] = {13, 12, 7, 6}; +byte colPins[KEYPAD_COLS] = {5, 4, 3, 2}; + +Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, KEYPAD_ROWS, KEYPAD_COLS); + +// +// Days of week +// + +char weekLetters[] = {' ', 'M', 'T', 'W', 'H', 'F', 'S', 'U'}; + +int toggleDayIndex; + +void toggleWeekDayEnable() +{ + weekLetters[toggleDayIndex] = (isupper(weekLetters[toggleDayIndex])) + ? tolower(weekLetters[toggleDayIndex]) + : toupper(weekLetters[toggleDayIndex]); +} + +// +// Alarms +// + +class TAlarm +{ + public: + char *day, hour, minutes, dispenserId = -1; +}; + +TAlarm alarms[WEEK_LENGTH * ALARMS_PER_DAY]; + +int firstEmptyAlarmIndex() +{ + for(i = 0; i < sizeof(alarms)/sizeof(*alarms); i++) + { + if (alarms[i].dispenserId == -1) break; + } + return (i == sizeof(alarms)/sizeof(*alarms)) ? -1 : i; +} + +int nextAlarmIndex(int currIndex) +{ + for(i = currIndex + 1; i < sizeof(alarms)/sizeof(*alarms); i++) + { + if (alarms[i].dispenserId != -1) break; + } + return (i == sizeof(alarms)/sizeof(*alarms)) ? -1 : i; +} + +int prevAlarmIndex(int currIndex) +{ + for(i = currIndex - 1; i >= 0; i--) + { + if (alarms[i].dispenserId != -1) break; + } + return i; +} + +/* Alarming */ + +bool buttonIsPressed() +{ + return digitalRead(BTN_PIN); +} + +void alarm(unsigned long timeSinceActivation) +{ + while(buttonIsPressed()) + { + digitalWrite(ALARM_PIN, HIGH); + delay(100); + } + digitalWrite(ALARM_PIN, LOW); +} + +/* Dispensing */ + +void dispensePill(char dispenserNumber) { + digitalWrite(DISPENSER_PIN, HIGH); + delayMicroseconds(dispenserNumber * 1000); + digitalWrite(DISPENSER_PIN, LOW); +} + +// +// Menus +// + +void (*menu)(char); +bool confirm; + +int alarmIndex = -1; + +/* Alarm edit submenu */ + +char formatted[LCD_COLS + 1]; + +enum awaitingValue +{ + awaitingNothing = -1, awaitingDay, awaitingHour, awaitingMinutes, awaitingDispenser, awaitingDeletion +}; + +#define AV_CONFIRM 0 +#define AV_AWAITING 1 +#define AV_VALUE 2 + +char awaitVal[3] = {false, awaitingNothing, -1}; + +void awaitValReset() +{ + awaitVal[AV_CONFIRM] = false, awaitVal[AV_AWAITING] = awaitingNothing, awaitVal[AV_VALUE] = -1; +} + +bool validateValue(int value) +{ + switch(awaitVal[AV_AWAITING]) + { + case awaitingDay: return value >= 1 && value <= WEEK_LENGTH; + case awaitingHour: return value >= 0 && value <= 23; + case awaitingMinutes: return value >= 0 && value <= 59; + case awaitingDispenser:return value >= 0; + } +} + +void setAVNum(char key) +{ + if (validateValue((awaitVal[AV_VALUE] * 10) + (key - '0'))) + { + awaitVal[AV_VALUE] *= 10; + awaitVal[AV_VALUE] += key - '0'; + editingSubMenu('#'); + } + else if (awaitVal[AV_VALUE] < 0 && validateValue(key - '0')) + { + awaitVal[AV_VALUE] = key - '0'; + } + else + { + switch(awaitVal[AV_AWAITING]) + { + case awaitingDay: printErrorToLCD(MSG_ERR_DAY); break; + case awaitingHour: printErrorToLCD(MSG_ERR_HOUR); break; + case awaitingMinutes: printErrorToLCD(MSG_ERR_MIN); break; + case awaitingDispenser: printErrorToLCD(MSG_ERR_DIS); break; + } + + awaitValReset(); + alarmMenu('~'); editingSubMenu('~'); + } +} + +void editingSubMenu(char key) +{ + switch(key) + { + case 'A': awaitVal[AV_AWAITING] = awaitingDay; printToLCD(1, MSG_DAY_NUM); + break; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': case '0': + if (awaitVal[AV_AWAITING] != awaitingNothing) setAVNum(key); + if (awaitVal[AV_AWAITING] == awaitingDay) editingSubMenu('#'); + + break; + case 'B': awaitVal[AV_AWAITING] = awaitingHour; printToLCD(1, MSG_HOUR); break; + case 'C': + if (awaitVal[AV_CONFIRM]) + { + editingSubMenu('*'); + } + else + { + awaitVal[AV_AWAITING] = awaitingMinutes; + printToLCD(1, MSG_MINUTES); + } + + break; + case 'D': awaitVal[AV_AWAITING] = awaitingDispenser; printToLCD(1, MSG_DISPENSER); break; + case '#': + if (awaitVal[AV_AWAITING] == awaitingNothing) + { + awaitVal[AV_AWAITING] = awaitingDeletion; + } + + if (awaitVal[AV_CONFIRM]) + { + switch(awaitVal[AV_AWAITING]) + { + case awaitingDay: alarms[alarmIndex].day = &weekLetters[awaitVal[AV_VALUE]]; break; + case awaitingHour: alarms[alarmIndex].hour = awaitVal[AV_VALUE]; break; + case awaitingMinutes: alarms[alarmIndex].minutes = awaitVal[AV_VALUE]; break; + case awaitingDeletion: + case awaitingDispenser: alarms[alarmIndex].dispenserId = awaitVal[AV_VALUE]; break; + } + awaitValReset(); + editingSubMenu('*'); + } + else + { + awaitVal[AV_CONFIRM] = true; + printToLCD(1, MSG_CONFIRM); + } + + break; + case '*': awaitValReset(); menu = alarmMenu; menu('~'); break; + case '~': printToLCD(1, MSG_EDIT_TOOLS); break; + } +} + +/* Alarm menu */ + +void printAlarmMenu() +{ + if (alarmIndex < 0) + { + printToLCD(0, MSG_NO_ALRM_R1); + printToLCD(1, MSG_NO_ALRM_R2); + } + else + { + snprintf(formatted, LCD_COLS + 1, "%02i %c %02i:%02i %02i ", alarmIndex, *alarms[alarmIndex].day, alarms[alarmIndex].hour, alarms[alarmIndex].minutes, alarms[alarmIndex].dispenserId); + printToLCD(0, formatted); + + snprintf(formatted, LCD_COLS + 1, "%c C %c", (prevAlarmIndex(alarmIndex) >= 0) ? 'A' : 'a' , (alarmIndex < nextAlarmIndex(alarmIndex)) ? 'B' : 'b'); + printToLCD(1, formatted); + } +} + +void alarmMenu(char key) +{ + switch(key) + { + case 'A': + if (prevAlarmIndex(alarmIndex) >= 0) + { + alarmIndex--; + printAlarmMenu(); + } + + break; + case 'B': + if (alarmIndex < nextAlarmIndex(alarmIndex)) + { + alarmIndex++; + printAlarmMenu(); + } + + break; + case 'C': menu = editingSubMenu; menu('~'); break; + case '#': + alarmIndex = firstEmptyAlarmIndex(); + menu = editingSubMenu; + alarmMenu('~'); menu('D'); + break; + case '*': alarmIndex = 0; menu = mainMenu; menu('~'); break; + case '~': printAlarmMenu(); break; + } +} + +/* Week menu */ + +bool waitForDayNum; + +void printWeekMenu() +{ + sprintf(formatted," %c %c %c %c %c %c %c ", weekLetters[1], weekLetters[2], weekLetters[3], weekLetters[4], weekLetters[5], weekLetters[6], weekLetters[7]); + printToLCD(0, formatted); + + if (waitForDayNum) printToLCD(1, MSG_DAY_NUM); + else if (confirm) printToLCD(1, MSG_CONFIRM); + else printToLCD(1, MSG_WEEK_TOGGLE); +} + +void weekMenu(char key) +{ + switch(key) + { + case 'A': waitForDayNum = true; weekMenu('~'); break; + case 'C': confirm = false; weekMenu('~'); break; + case '#': + if (confirm) + { + toggleWeekDayEnable(); + confirm = false; + } + weekMenu('~'); + + break; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': + if (waitForDayNum) + { + toggleDayIndex = key - '0'; + waitForDayNum = false; confirm = true; + } + weekMenu('~'); + + break; + case '*': menu = mainMenu; menu('~'); break; + case '~': printWeekMenu(); break; + } +} + +/* Main menu */ + +void mainMenu(char key) +{ + switch(key) + { + case 'A': menu = weekMenu; weekMenu('~'); break; + case 'B': menu = alarmMenu; alarmMenu('~'); break; + default: printToLCD(0, MSG_MAIN_R1); printToLCD(1, MSG_MAIN_R2); break; + } +} + +// +// Main logic +// + +void setup() +{ + lcd.begin(LCD_COLS, LCD_ROWS); + + menu = mainMenu; + mainMenu('~'); +} + +char key; + +void loop() +{ + key = keypad.getKey(); // getKey function is too quick, once you get the key, the second time you get it it will return null + if (key != '\0') + { + menu(key); + } + else if(buttonIsPressed()) //TODO: if button is pressed AND time for alarm + { + dispensePill(3); // dispense correct pill + alarm(millis()); + } + delay(100); //TinkerCad optimization +} |
