/* theta.eu.org Systems GCSE CAT code * Copyright (c) 2018 eta; all rights reserved */ #include #include #include #include #define OLED_RESET 11 #define BODGEPIN 7 #define ROTA 13 #define ROTB 12 #define BTNB 10 #define BTNA 9 #define SHDAT 6 #define SPKR 2 #define SHLCH 5 #define SHCLK 4 #define BATLVL A2 #define MIC A3 #define RED 0 #define GREEN 1 #define BLUE 2 #define BATTHRESH 250 static char* VERSION = "1.0.0"; Encoder rotato(ROTA, ROTB); AXE133Y OLED = AXE133Y(BODGEPIN); unsigned long thresh; byte pitch; struct led_sr { uint8_t data1; uint8_t data2; }; void led_sr_change(struct led_sr *ptr, uint8_t led, uint8_t col, bool enable) { uint16_t data = 0; if (col == RED) data |= 0b00000001; if (col == GREEN) data |= 0b00000010; if (col == BLUE) data |= 0b00000100; data = data << (led * 3); if (enable) { ptr->data1 ^= (uint8_t) data; ptr->data2 ^= (uint8_t) (data >> 7); } else { ptr->data1 ^= (uint8_t) ~data; ptr->data2 ^= (uint8_t) ~(data >> 7); } sensible_shift(ptr->data1, ptr->data2); } void sensible_shift(byte data1, byte data2) { digitalWrite(SHLCH, 0); shiftOut(SHDAT, SHCLK, ~data1); shiftOut(SHDAT, SHCLK, ~data2); digitalWrite(SHLCH, 1); } struct led_sr led; volatile int rot = 0; void rotato_isr() { rot = rotato.read(); } void init_serial() { Serial.begin(9600); Serial.println("eta - S&C CAT code"); Serial.print("Version "); Serial.println(VERSION); } void display_version() { OLED.clearScreen(); OLED.cursorHome(1); OLED.print("S&C CAT code"); OLED.cursorHome(0); OLED.print("Version "); OLED.print(VERSION); } void init_rotato() { Timer1.attachInterrupt(rotato_isr, 1000); } void deinit_rotato() { Timer1.detachInterrupt(); } void init_pins() { pinMode(BATLVL, INPUT); pinMode(MIC, INPUT); pinMode(SHLCH, OUTPUT); pinMode(SHDAT, OUTPUT); pinMode(SHCLK, OUTPUT); pinMode(BODGEPIN, OUTPUT); pinMode(BTNA, INPUT_PULLUP); pinMode(ROTA, INPUT_PULLUP); pinMode(ROTB, INPUT_PULLUP); pinMode(BTNB, INPUT_PULLUP); } unsigned long EEPROMReadlong(long address) { unsigned long four = EEPROM.read(address); unsigned long three = EEPROM.read(address + 1); unsigned long two = EEPROM.read(address + 2); unsigned long one = EEPROM.read(address + 3); return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF); } void EEPROMWritelong(int address, unsigned long value) { byte four = (value & 0xFF); byte three = ((value >> 8) & 0xFF); byte two = ((value >> 16) & 0xFF); byte one = ((value >> 24) & 0xFF); EEPROM.write(address, four); EEPROM.write(address + 1, three); EEPROM.write(address + 2, two); EEPROM.write(address + 3, one); } #define RETURN (void (*)()) 0xDEADBEEF void setup() { init_pins(); init_serial(); thresh = EEPROMReadlong(1); pitch = EEPROM.read(0); for (unsigned int i = 0; i < 6; i++) { led_sr_change(&led, i, RED, true); } display_version(); delay(1000); if (analogRead(BTNA) == LOW) { threshc(1024); } for (unsigned int i = 0; i < 6; i++) { led_sr_change(&led, i, RED, false); } online(); } void online() { led = {0}; led_sr_change(&led, 0, BLUE, true); OLED.clearScreen(); OLED.cursorHome(1); OLED.print("** ONLINE **"); OLED.cursorHome(0); OLED.print("press for menu"); } void updated() { OLED.clearScreen(); OLED.cursorHome(1); OLED.print("** UPDATED **"); delay(1000); } void pitchc(byte val) { EEPROM.write(0, val); pitch = val; updated(); tone(SPKR, pitch, 1000); } void pitch_low() { pitchc(220); } void pitch_mid() { pitchc(440); } void pitch_high() { pitchc(880); } void change_pitch() { static const char *t[4] = {"Go Back", "Low", "Medium", "High"}; static const int t_len = 4; static void (*f[4])() = {RETURN, &pitch_high, &pitch_mid, &pitch_low}; show_menu("Pitch =?", t, t_len, f); } void threshc(long val) { EEPROMWritelong(1, val); thresh = val; updated(); } void thresh_low() { threshc(555); } void thresh_mid() { threshc(565); } void thresh_high() { threshc(575); } void change_thresh() { static const char *t[4] = {"Go Back", "Low", "Medium", "High"}; static const int t_len = 4; static void (*f[4])() = {RETURN, &thresh_high, &thresh_mid, &thresh_low}; show_menu("Sensitivity =?", t, t_len, f); } void exciting() { for (unsigned int j = 0; j < 5; j++) { tone(SPKR, pitch, 1000); for (unsigned int i = 0; i < 5; i++) { sensible_shift(0, 0); delay(100); sensible_shift(~0, ~0); delay(100); } } for (unsigned int i = 0; i < 5; i++) { sensible_shift(0, 0); } } void batfail() { led = {0}; for (unsigned int i = 0; i < 6; i++) { led_sr_change(&led, i, RED, true); } OLED.clearScreen(); OLED.cursorHome(1); OLED.print("** WARNING **"); OLED.cursorHome(0); OLED.print("LOW BATTERY!"); } void loop() { deinit_rotato(); led = {0}; led_sr_change(&led, 0, BLUE, true); if (analogRead(BATLVL) < BATTHRESH) { batfail(); tone(SPKR, pitch, 1000); delay(1000); } if (analogRead(MIC) > thresh) { exciting(); delay(1000); online(); } if (digitalRead(BTNA) == LOW) { static const char *t[4] = {"Go Back", "Threshold", "Pitch", "Test"}; static const int t_len = 4; static void (*f[4])() = {RETURN, &change_thresh, &change_pitch, &exciting}; show_menu("MAIN MENU", t, t_len, f); deinit_rotato(); delay(1000); online(); } } void show_menu(const char *mname, const char **titles, int titles_len, void (**fns)()) { unsigned int idx = 0; led = {0}; led_sr_change(&led, 0, RED, true); start: { while (digitalRead(BTNA) == LOW) {} deinit_rotato(); if (idx >= titles_len) { idx = 0; } if (idx < 0) { idx = titles_len - 1; } OLED.clearScreen(); OLED.cursorHome(1); OLED.print("* "); OLED.print(mname); OLED.cursorHome(0); OLED.print("> "); OLED.print(titles[idx]); init_rotato(); int rot_old = rot; for (;;) { if ((rot - rot_old) > 10) { idx += 1; goto start; } if ((rot - rot_old) < -10) { idx -= 1; goto start; } if (digitalRead(BTNA) == LOW) { while (digitalRead(BTNA) == LOW) {} if (fns[idx] == RETURN) { goto e; } deinit_rotato(); (fns[idx])(); init_rotato(); idx = 0; goto start; } } e: { } } } void shiftOut(int myDataPin, int myClockPin, byte myDataOut) { // This shifts 8 bits out MSB first, //on the rising edge of the clock, //clock idles low //internal function setup int i = 0; int pinState; pinMode(myClockPin, OUTPUT); pinMode(myDataPin, OUTPUT); //clear everything out just in case to //prepare shift register for bit shifting digitalWrite(myDataPin, 0); digitalWrite(myClockPin, 0); //for each bit in the byte myDataOut� //NOTICE THAT WE ARE COUNTING DOWN in our for loop //This means that %00000001 or "1" will go through such //that it will be pin Q0 that lights. for (i = 7; i >= 0; i--) { digitalWrite(myClockPin, 0); //if the value passed to myDataOut and a bitmask result // true then... so if we are at i=6 and our value is // %11010100 it would the code compares it to %01000000 // and proceeds to set pinState to 1. if ( myDataOut & (1 << i) ) { pinState = 1; } else { pinState = 0; } //Sets the pin to HIGH or LOW depending on pinState digitalWrite(myDataPin, pinState); //register shifts bits on upstroke of clock pin digitalWrite(myClockPin, 1); //zero the data pin after shift to prevent bleed through digitalWrite(myDataPin, 0); } //stop shifting digitalWrite(myClockPin, 0); }