/* theta.eu.org Systems GCSE CAT code * Copyright (c) 2018 eta; all rights reserved * * This version is adapted for use on a Maple Mini. */ #include #include #include // Core graphics library #include // Hardware-specific library for ST7735 #include // Hardware-specific library for ST7789 #include #define BODGEPIN 13 #define ROTA 19 #define ROTB 20 #define BTNB 22 #define BTNA 28 #define SHDAT 29 #define SPKR 27 #define SHLCH 30 #define SHCLK 31 #define BATLVL 5 #define MIC 8 #define BTNUP 17 #define BTNDOWN 18 #define TFT_MOSI 12 #define TFT_SCLK 14 #define TFT_CS 11 #define TFT_RST 10 #define TFT_DC 9 #define RED 0 #define GREEN 1 #define BLUE 2 #define BATTHRESH 250 #define PITCH_ADDR 0x01 #define THRESH_ADDR 0x02 static char* VERSION = "1.1.0-maplemini"; struct led_sr { uint8_t data1; uint8_t data2; }; Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST); AXE133Y OLED = AXE133Y(BODGEPIN); uint16_t thresh; uint16_t pitch; struct led_sr led; HardwareTimer timer(2); 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); } /* IMPORTED ROTARY ENCODER CODE */ #define MAXENCODERS 1 volatile int encstate[MAXENCODERS]; volatile int encflag[MAXENCODERS]; boolean A_set[MAXENCODERS]; boolean B_set[MAXENCODERS]; volatile int16_t encoderpos[MAXENCODERS]; volatile int encodertimer = millis(); // acceleration measurement int encoderpinA[MAXENCODERS] = {ROTA}; // pin array of all encoder A inputs int encoderpinB[MAXENCODERS] = {ROTB}; // pin array of all encoder B inputs unsigned int lastEncoderPos[MAXENCODERS]; // timer #define ENCODER_RATE 1000 // in microseconds; // ********encoder function void readEncoders() { for (byte counter = 0; counter < MAXENCODERS; counter++) { if ( (gpio_read_bit(PIN_MAP[encoderpinA[counter]].gpio_device, PIN_MAP[encoderpinA[counter]].gpio_bit) ? HIGH : LOW) != A_set[counter] ) { A_set[counter] = !A_set[counter]; if ( A_set[counter] && !B_set[counter] ) { if (millis() - encodertimer > 3) encoderpos[counter] += 1; else encoderpos[counter] += 5; } encodertimer = millis(); } if ( (gpio_read_bit(PIN_MAP[encoderpinB[counter]].gpio_device, PIN_MAP[encoderpinB[counter]].gpio_bit) ? HIGH : LOW) != B_set[counter] ) { B_set[counter] = !B_set[counter]; if ( B_set[counter] && !A_set[counter] ) if (millis() - encodertimer > 3) encoderpos[counter] -= 1; else encoderpos[counter] -= 5; encodertimer = millis(); } } } void init_rotato() { encodertimer = millis(); // acceleration measurement for (byte counter = 0; counter < MAXENCODERS; counter++) { encstate[counter] = HIGH; encflag[counter] = HIGH; A_set[counter] = false; B_set[counter] = false; encoderpos[counter] = 0; pinMode(encoderpinA[counter], INPUT_PULLUP); pinMode(encoderpinB[counter], INPUT_PULLUP); lastEncoderPos[counter] = 1; } // timer setup for encoder timer.pause(); timer.setPeriod(ENCODER_RATE); // in microseconds timer.setChannel1Mode(TIMER_OUTPUT_COMPARE); timer.setCompare(TIMER_CH1, 1); // Interrupt 1 count after each update timer.attachCompare1Interrupt(readEncoders); timer.refresh(); timer.resume(); Serial1.println("Encoder initialization completed"); } /* END IMPORTED ENCODER CODE */ void init_serial() { Serial1.begin(9600); Serial1.println("eta - S&C CAT code"); Serial1.print("Version "); Serial1.println(VERSION); digitalWrite(LED_BUILTIN, HIGH); } void display_version() { Serial1.println("OLED - displaying version"); tft_clear(); tft.println("S&C CAT code"); tft.print("Version "); tft.println(VERSION); } void init_pins() { pinMode(LED_BUILTIN, OUTPUT); 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); pinMode(BTNUP, INPUT_PULLUP); pinMode(BTNDOWN, INPUT_PULLUP); } void init_tft() { tft.initR(INITR_144GREENTAB); // Init ST7735R chip, green tab tft.fillScreen(ST77XX_BLACK); } void tft_clear() { tft.fillScreen(ST77XX_BLACK); tft.setTextColor(ST77XX_WHITE); tft.setCursor(0, 0); tft.setTextSize(1); } #define RETURN (void (*)()) 0xDEADBEEF void setup() { init_pins(); init_serial(); //init_rotato(); init_tft(); EEPROM.PageBase0 = 0x801F000; EEPROM.PageBase1 = 0x801F800; EEPROM.PageSize = 0x400; uint16_t status0 = EEPROM.init(); uint16_t status1 = EEPROM.read(THRESH_ADDR, &thresh); uint16_t status2 = EEPROM.read(PITCH_ADDR, &pitch); Serial1.print("EEPROM init status "); Serial1.println(status0); Serial1.print("Pitch setting: "); Serial1.print(pitch); Serial1.print(" status "); Serial1.println(status1); Serial1.print("Threshold setting: "); Serial1.print(thresh); Serial1.print(" status "); Serial1.println(status2); for (unsigned int i = 0; i < 6; i++) { led_sr_change(&led, i, RED, true); } display_version(); delay(1000); if (digitalRead(BTNA) == LOW) { Serial1.println("*** Button held on boot - initializing EEPROM"); uint16_t Status = EEPROM.format(); Serial1.print("*** Initialization status "); Serial1.println(Status); threshc(65535); pitch_high(); } for (unsigned int i = 0; i < 6; i++) { led_sr_change(&led, i, RED, false); } online(); } void credits() { display_version(); tft.println("a theta.eu.org project"); delay(5000); } void log_mic() { int start = millis(); uint16_t maxval = 0; uint16_t minval = 65535; tft.println("Yell into the mic"); while ((millis() - start) < 3000) { uint16_t val = analogRead(MIC); if (val > maxval) { maxval = val; } if (val < minval) { minval = val; } } tft.print("Max value: "); tft.println(maxval); tft.print("Min value: "); tft.println(minval); delay(2000); } void online() { Serial1.println("ONLINE - press button for menu"); led = {0}; led_sr_change(&led, 0, BLUE, true); tft_clear(); tft.setTextColor(ST77XX_GREEN); tft.setTextSize(2); tft.println("ONLINE"); tft.setTextColor(ST77XX_WHITE); tft.setTextSize(1); tft.println("press for menu"); } void updated() { Serial1.println("UPDATED"); tft_clear(); tft.setTextColor(ST77XX_BLUE); tft.setTextSize(2); tft.println("UPDATED"); delay(1000); } void pitchc(uint16_t val) { uint16_t Status = EEPROM.write(PITCH_ADDR, val); Serial1.print("Changing pitch to "); Serial1.print(val); Serial1.print(": status "); Serial1.println(Status); 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_low, &pitch_mid, &pitch_high}; show_menu("Pitch =?", t, t_len, f); } void threshc(uint16_t val) { uint16_t Status = EEPROM.write(THRESH_ADDR, val); Serial1.print("Changing threshold to "); Serial1.print(val); Serial1.print(": status "); Serial1.println(Status); thresh = val; updated(); } void thresh_low() { threshc(700); } void thresh_mid() { threshc(800); } void thresh_high() { threshc(1000); } 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() { Serial1.println("Activating speaker!"); 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() { Serial1.println("LOW BATTERY"); led = {0}; for (unsigned int i = 0; i < 6; i++) { led_sr_change(&led, i, RED, true); } tft_clear(); tft.println("** WARNING **"); tft.println("LOW BATTERY!"); } void loop() { led = {0}; led_sr_change(&led, 0, BLUE, true); if (analogRead(BATLVL) < BATTHRESH) { batfail(); tone(SPKR, pitch, 1000); delay(1000); } if (analogRead(MIC) > thresh) { Serial1.print("Mic level "); Serial1.print(analogRead(MIC)); Serial1.print(" exceeds threshold "); Serial1.println(thresh); exciting(); delay(1000); online(); } if (digitalRead(BTNA) == LOW) { static const char *t[6] = {"Go Back", "Threshold", "Pitch", "Test", "Debug Mic", "Version"}; static const int t_len = 6; static void (*f[6])() = {RETURN, &change_thresh, &change_pitch, &exciting, &log_mic, &credits}; show_menu("MAIN MENU", t, t_len, f); delay(1000); online(); } } void show_menu(const char *mname, const char **titles, int titles_len, void (**fns)()) { Serial1.print("Entering menu: "); Serial1.println(mname); unsigned int idx = 0; led = {0}; led_sr_change(&led, 0, RED, true); start: { while (digitalRead(BTNA) == LOW) {} if (idx >= titles_len) { idx = 0; } if (idx < 0) { idx = titles_len - 1; } tft_clear(); tft.print("* "); tft.println(mname); for (unsigned int i = 0; i < titles_len; i++) { if (idx == i) { tft.print("> "); } else { tft.print(" "); } tft.println(titles[i]); } Serial1.print("> "); Serial1.print(titles[idx]); Serial1.print(" ["); Serial1.print(idx + 1); Serial1.print(" of "); Serial1.print(titles_len); Serial1.println("]"); //int rot_old = encoderpos[0]; for (;;) { /*if ((encoderpos[0] - rot_old) > 10) { idx += 1; goto start; } if ((encoderpos[0] - rot_old) < -10) { idx -= 1; goto start; }*/ if (digitalRead(BTNUP) == LOW) { while (digitalRead(BTNUP) == LOW) {} idx -= 1; goto start; } if (digitalRead(BTNDOWN) == LOW) { while (digitalRead(BTNDOWN) == LOW) {} idx += 1; goto start; } if (digitalRead(BTNA) == LOW) { while (digitalRead(BTNA) == LOW) {} Serial1.println("Executing item"); if (fns[idx] == RETURN) { goto e; } (fns[idx])(); 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); }