// Open Stuff: // Implementing: // - Calibrate Temp measurement #include #include #include #include #include #include #ifndef NOP #define NOP __asm__("nop\n\t") #endif #define NUM_LEDS 4 #define DATA_PIN 11 #define led 12 #define BEEP_PIN 17 #define cut0 A0 #define cut1 A1 #define cut2 A2 uint8_t i; CRGBArray leds; // Module connection pins (Digital Pins) #define CLK 10 #define DIO 9 TM1637Display ndis(CLK, DIO); char keys[4][3]={ {'1','2','3'}, {'4','5','6'}, {'7','8','9'}, {'*','0','#'}, }; byte rowPins[4] = {2,3,4,5}; byte colPins[3] = {6,7,8}; Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, 4, 3 ); U8G2_SSD1306_128X64_NONAME_2_HW_I2C display(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE); #define max_timerindex 11 uint16_t timerval[max_timerindex] = {30, 60, 90, 120, 180, 240, 300, 360, 420, 480, 600}; uint8_t timerindex = 4; #define max_penaltyindex 11 uint8_t penaltyval[max_penaltyindex] = {0, 5, 10, 15, 20, 30, 40, 50, 60, 90, 120}; uint8_t penaltyindex = 0; uint16_t timer; uint16_t tdisplay; unsigned long startmillis; bool initmillis = false; volatile uint16_t tint_h = 500; volatile uint16_t tint_f = 1000; uint16_t defusecode; uint8_t defdigit; uint16_t definput; bool defchecked; #define GOOD 1 #define BAD 2 #define VERYBAD 3 volatile uint8_t cutopt[] {GOOD, BAD, VERYBAD}; volatile bool disarmed = false; volatile bool tint_fast = false; volatile bool detonate = false; float bgresult, tpresult, batresult; ResponsiveAnalogRead bgresp(A6, true, 0.05); ResponsiveAnalogRead tpresp(A6, true, 0.05); ResponsiveAnalogRead batresp(A6, true, 0.05); const uint8_t SEG_CLEAR[] = { 0x00, 0x00, 0x00, 0x00 }; const uint8_t SEG_OFF[] = { SEG_G, // - SEG_G, // - SEG_G, // - SEG_G // - }; int analogReadBG(){ uint8_t low, high; ADMUX = 0; // For 168/328 boards // REFS1 REFS0 --> 0 1, AVcc internal ref. -Selects AVcc external reference // MUX3 MUX2 MUX1 MUX0 --> 1110 1.1V (VBG) -Selects channel 14, bandgap voltage, to measure ADMUX = (0 << REFS1) | (1 << REFS0) | (0 << ADLAR) | (1 << MUX3) | (1 << MUX2) | (1 << MUX1) | (0 << MUX0); delay(2); bitSet(ADCSRA, ADSC); while (bit_is_set(ADCSRA, ADSC)); low = ADCL; high = ADCH; return (high << 8) | low; } int analogReadTP(){ uint8_t low, high; ADMUX = 0; // For 168/328 boards // REFS1 REFS0 --> 1 1, Internal 1.1V Voltage Reference -Selects bandgap voltage // MUX3 MUX2 MUX1 MUX0 --> 1000 Internal Temperature Sensor -Selects channel 8, temperature sensor, to measure ADMUX = (1 << REFS1) | (1 << REFS0) | (0 << ADLAR) | (1 << MUX3) | (0 << MUX2) | (0 << MUX1) | (0 << MUX0); delay(7); bitSet(ADCSRA, ADSC); while (bit_is_set(ADCSRA, ADSC)); low = ADCL; high = ADCH; return ((high << 8) | low) - 330; } void initlcd() { display.setFont(u8g2_font_profont12_tr); display.setFontRefHeightExtendedText(); display.setDrawColor(2); display.setFontPosTop(); display.setFontDirection(0); } uint16_t pow10(uint8_t n) { switch(n) { case 0: return 1; break; case 1: return 10; break; case 2: return 100; break; case 3: return 1000; break; default: return 0; break; } } void explosion() { const uint8_t frame[10][4] = { 0x00, SEG_B | SEG_C, SEG_E | SEG_F, 0x00, 0x00, SEG_B | SEG_C | SEG_G, SEG_E | SEG_F | SEG_G, 0x00, 0x00, 0b01111111, 0b01111111, 0x00, SEG_B | SEG_C |SEG_G, 0b01111111, 0b01111111, SEG_E | SEG_F | SEG_G, 0b01111111, 0b01111111, 0b01111111, 0b01111111, 0b01111111, SEG_A | SEG_D | SEG_E | SEG_F, SEG_A | SEG_B | SEG_C | SEG_D, 0b01111111, SEG_A | SEG_D | SEG_E | SEG_F, 0x00, 0x00, SEG_A | SEG_B | SEG_C | SEG_D, SEG_A | SEG_D | SEG_E | SEG_F, 0x00, 0x00, SEG_A | SEG_B | SEG_C | SEG_D, SEG_CLEAR[0], SEG_CLEAR[1], SEG_CLEAR[2], SEG_CLEAR[3], SEG_B | SEG_C | SEG_D | SEG_E | SEG_G, SEG_A | SEG_D | SEG_E | SEG_F | SEG_G, SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G, SEG_B | SEG_C | SEG_D | SEG_E | SEG_G }; const uint8_t delwait = 20; uint8_t i; // Make sure everything is switched off digitalWrite(led, LOW); digitalWrite(LED_BUILTIN, LOW); // But beep through the animation digitalWrite(BEEP_PIN, HIGH); display.firstPage(); do { initlcd(); display.setCursor(20, 22); display.print("< DETONATING >"); } while ( display.nextPage() ); for(i=0; i < 9; i++) { switch(i) { case 0: case 7: case 8: ndis.setBrightness(0); break; case 1: ndis.setBrightness(1); break; case 2: ndis.setBrightness(3); break; case 3: case 5: ndis.setBrightness(5); break; case 4: ndis.setBrightness(7); break; case 6: ndis.setBrightness(2); break; } ndis.setSegments(frame[i]); leds[0].setRGB(i, i*2, 0); leds[1].setRGB(i*2, i*4, i); leds[2].setRGB(i*2, i*4, i); leds[3].setRGB(i, i*2, 0); FastLED.show(); delay_init(); delay_waste(delwait); } delay_init(); delay_waste(500); ndis.setBrightness(7); ndis.setSegments(frame[9]); digitalWrite(BEEP_PIN, LOW); leds[0].setRGB(0, 0, 0); leds[1].setRGB(0, 0, 0); leds[2].setRGB(0, 0, 0); leds[3].setRGB(0, 0, 0); FastLED.show(); display.firstPage(); do { initlcd(); display.setCursor(26, 22); display.print("You are dead"); display.setCursor(24, 32); display.print("Press any key"); display.drawBox(0, 0, 128, 64); } while ( display.nextPage() ); } void safe() { // Display leftover time tdisplay = ((timer / 60) * 100); tdisplay += timer % 60; ndis.showNumberDecEx(tdisplay, 64, true); // Make sure everything is switched off digitalWrite(led, LOW); digitalWrite(LED_BUILTIN, LOW); digitalWrite(BEEP_PIN, LOW); for(uint8_t i=0; i < 4; i++) { leds[i].setRGB(0,0,0); } FastLED.show(); display.firstPage(); do { initlcd(); display.setCursor(26, 22); display.print("< DISARMED >"); display.setCursor(24, 32); display.print("Press any key"); display.drawBox(0, 0, 128, 64); } while ( display.nextPage() ); delay_init(); delay_waste(500); } void initiate() { display.firstPage(); do { initlcd(); display.setCursor(26, 0); display.print("Enter Code"); display.setCursor(26, 10); display.print("* Delete"); display.setCursor(26, 20); display.print("# Options"); } while ( display.nextPage() ); ndis.setBrightness(7); // (0-7) ndis.setSegments(SEG_OFF); uint8_t digit = 0; uint16_t dinput = 0; while(digit < 4) { char key = keypad.getKey(); if(key != NO_KEY) { dinput *= 10; digit++; switch(key) { case '1': dinput += 1; break; case '2': dinput += 2; break; case '3': dinput += 3; break; case '4': dinput += 4; break; case '5': dinput += 5; break; case '6': dinput += 6; break; case '7': dinput += 7; break; case '8': dinput += 8; break; case '9': dinput += 9; break; case '0': dinput += 0; break; case '#': optionsmenu(); display.firstPage(); do { initlcd(); display.setCursor(26, 0); display.print("Enter Code"); display.setCursor(26, 10); display.print("* Back"); display.setCursor(26, 20); display.print("# Options"); } while ( display.nextPage() ); dinput = 0; digit = 0; break; case '*': if(digit > 1) { dinput /= 100; digit--; digit--; } else { digit--; } break; } ndis.setSegments(SEG_OFF); if(dinput > 0) { ndis.showNumberDecEx(dinput, 0, true, digit, 3-(digit-1)); } else { if(digit>0) { ndis.showNumberDecEx(0, 0, true, digit, 3-(digit-1)); } else { ndis.setSegments(SEG_OFF); } } } } tint_h = 500; tint_f = 1000; tint_fast = false; disarmed = false; detonate = false; definput = 0; defdigit = 0; switch((dinput + analogRead(A5)) % 6) { case 0: cutopt[0] = GOOD; cutopt[1] = BAD; cutopt[2] = VERYBAD; break; case 1: cutopt[0] = GOOD; cutopt[1] = VERYBAD; cutopt[2] = BAD; break; case 2: cutopt[0] = BAD; cutopt[1] = VERYBAD; cutopt[2] = GOOD; break; case 3: cutopt[0] = BAD; cutopt[1] = GOOD; cutopt[2] = VERYBAD; break; case 4: cutopt[0] = VERYBAD; cutopt[1] = GOOD; cutopt[2] = BAD; break; case 5: cutopt[0] = VERYBAD; cutopt[1] = BAD; cutopt[2] = GOOD; break; } timer = timerval[timerindex]; defusecode = dinput; defchecked = false; attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(cut0), detcut0, RISING); attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(cut1), detcut1, RISING); attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(cut2), detcut2, RISING); display.firstPage(); do { initlcd(); display.setCursor(37, 0); display.print("< ARMED >"); } while ( display.nextPage() ); } void waitkey() { char key = getkey(); } char getkey() { char key = keypad.getKey(); while(key == NO_KEY) { key = keypad.getKey(); } return key; } void delay_init() { startmillis=millis(); initmillis=true; } bool delay_done(uint16_t wastetime) { if(initmillis) { return !(millis() - startmillis < wastetime); } else { return true; } } void delay_waste(uint16_t wastetime) { if(initmillis) { while(!delay_done(wastetime)) { NOP; } initmillis=false; } } void delay_waste_cont(uint16_t wastetime) { if(initmillis) { delay_waste(wastetime); initmillis=true; } } void optionsmenu() // TODO: Write Menu code, change settings, display values { bool optionswitch = true; display.firstPage(); do { initlcd(); display.setCursor(32, 0); display.print("< OPTIONS >"); if(optionswitch) { display.setCursor(38, 10); display.print("Set Timer"); } else { display.setCursor(32, 10); display.print("Set Penalty"); } display.setCursor(0, 30); display.print("2 Up, 8 Down"); display.setCursor(0, 40); display.print("4 or 6 Select option"); display.setCursor(0, 50); display.print("* Cancel, # OK"); } while ( display.nextPage() ); char key = '-'; uint8_t temptimerindex = timerindex; uint8_t temppenaltyindex = penaltyindex; bool endoption = false; while(!endoption) { if(optionswitch) { tdisplay = ((timerval[temptimerindex] / 60) * 100); tdisplay += timerval[temptimerindex] % 60; } else { tdisplay = ((penaltyval[temppenaltyindex] / 60) * 100); tdisplay += penaltyval[temppenaltyindex] % 60; } ndis.showNumberDecEx(tdisplay, 64, true); key = getkey(); switch(key) { case '2': if(optionswitch) { if(temptimerindex < (max_timerindex - 1)) temptimerindex++; } else { if(temppenaltyindex < (max_penaltyindex -1)) temppenaltyindex++; } break; case '8': if(optionswitch) { if(temptimerindex > 0) temptimerindex--; } else { if(temppenaltyindex > 0) temppenaltyindex--; } break; case '4': case '6': optionswitch = !optionswitch; display.firstPage(); do { initlcd(); display.setCursor(32, 0); display.print("< OPTIONS >"); if(optionswitch) { display.setCursor(38, 10); display.print("Set Timer"); } else { display.setCursor(32, 10); display.print("Set Penalty"); } display.setCursor(0, 30); display.print("2 Up, 8 Down"); display.setCursor(0, 40); display.print("4 or 6 Select option"); display.setCursor(0, 50); display.print("* Cancel, # OK"); } while ( display.nextPage() ); break; case '*': endoption = true; break; case '#': endoption = true; timerindex = temptimerindex; penaltyindex = temppenaltyindex; break; } } } void checkinputs() { char key = keypad.getKey(); if(key != NO_KEY && defdigit < 4) { definput *= 10; defdigit++; switch(key) { case '1': definput += 1; break; case '2': definput += 2; break; case '3': definput += 3; break; case '4': definput += 4; break; case '5': definput += 5; break; case '6': definput += 6; break; case '7': definput += 7; break; case '8': definput += 8; break; case '9': definput += 9; break; case '0': definput += 0; break; case '#': case '*': if(defdigit > 1) { definput /= 100; defdigit--; defdigit--; } else { defdigit--; } break; } uint8_t i; for(i=0; i < 4; i++) { leds[i].setRGB(0,0,0); } for(uint8_t i=0; i < defdigit; i++) { leds[3-i].setRGB(0,0,16); } FastLED.show(); } } void detcut0(void) { switch(cutopt[0]) { case GOOD: disarmed=true; break; case BAD: if(!tint_fast) {tint_fast=true; tint_h /= 2; tint_f /= 2;} break; case VERYBAD: detonate=true; disarmed=true; break; } } void detcut1(void) { switch(cutopt[1]) { case GOOD: disarmed=true; break; case BAD: if(!tint_fast) {tint_fast=true; tint_h /= 2; tint_f /= 2;} break; case VERYBAD: detonate=true; disarmed=true; break; } } void detcut2(void) { switch(cutopt[2]) { case GOOD: disarmed=true; break; case BAD: if(!tint_fast) {tint_fast=true; tint_h /= 2; tint_f /= 2;} break; case VERYBAD: detonate=true; disarmed=true; break; } } void displayaval() { display.setCursor(0, 40); display.print("Temp: "); display.print(tpresult); display.setCursor(0, 52); display.print("Bat: "); display.print(batresult); display.print(" Vcc: "); display.print(bgresult); } void setup() { FastLED.addLeds(leds, NUM_LEDS); leds[0].setRGB(0, 0, 0); leds[1].setRGB(0, 0, 0); leds[2].setRGB(0, 0, 0); leds[3].setRGB(0, 0, 0); FastLED.show(); display.begin(); pinMode(LED_BUILTIN, OUTPUT); pinMode(BEEP_PIN, OUTPUT); pinMode(led, OUTPUT); pinMode(cut0, INPUT_PULLUP); pinMode(cut1, INPUT_PULLUP); pinMode(cut2, INPUT_PULLUP); pinMode(A6, INPUT); } void loop() { initiate(); while((timer > 0) && !disarmed) { delay_init(); // Read all analog inputs, calculate values. // This should be a good time because things are going to be noisy afterwards bgresp.update(analogReadBG()); bgresult=1126.4 / bgresp.getValue(); analogReference(DEFAULT); batresp.update(analogRead(A6)); batresult=(1.1 * batresp.getValue()) / bgresp.getValue(); tpresp.update(analogReadTP()); tpresult=tpresp.getValue(); // Now we have all analog readings, let's start with the noisy stuff if((defdigit == 4) && (defusecode == definput)) { disarmed = true; } if((defdigit == 4) && (!disarmed)) { for(uint8_t i=0; i < 4; i++) { if( (defusecode / pow10(i)) % 10 == (definput / pow10(i)) % 10) { leds[3-i].setRGB(16, 0, 0); } else { leds[3-i].setRGB(0, 16, 0); } } FastLED.show(); defchecked = true; } digitalWrite(led, HIGH); digitalWrite(LED_BUILTIN, HIGH); digitalWrite(BEEP_PIN, HIGH); tdisplay = ((timer / 60) * 100); tdisplay += timer % 60; ndis.showNumberDecEx(tdisplay, 64, true); display.firstPage(); do { initlcd(); display.setCursor(37, 0); display.print("< ARMED >"); displayaval(); } while ( display.nextPage() ); while(!delay_done(50) && !disarmed) { checkinputs(); } digitalWrite(led, LOW); digitalWrite(LED_BUILTIN, LOW); digitalWrite(BEEP_PIN, LOW); if(timer < 20) { while(!delay_done(150) && !disarmed) { checkinputs(); } digitalWrite(led, HIGH); digitalWrite(LED_BUILTIN, HIGH); digitalWrite(BEEP_PIN, HIGH); while(!delay_done(250) && !disarmed) { checkinputs(); } digitalWrite(led, LOW); digitalWrite(LED_BUILTIN, LOW); digitalWrite(BEEP_PIN, LOW); } while(!delay_done(tint_h) && !disarmed) { checkinputs(); } if(timer > 20) { ndis.showNumberDecEx(tdisplay, 0, true); while(!delay_done(tint_f) && !disarmed) { checkinputs(); } } else { ndis.setSegments(SEG_CLEAR); while(!delay_done(tint_f) && !disarmed) { checkinputs(); } } if(defdigit == 4 && !disarmed && defchecked) { defdigit = 0; definput = 0; for(uint8_t i=0; i < 4; i++) { leds[i].setRGB(0, 0, 0); } FastLED.show(); // Penalty for wrong try: reduce timer by set penalty (default: 10 seconds) if(timer > penaltyval[penaltyindex] + 1 ) { timer -= penaltyval[penaltyindex]; } else { timer = 1; } defchecked = false; } if(!disarmed) { timer--; } } detachPinChangeInterrupt(digitalPinToPinChangeInterrupt(cut0)); detachPinChangeInterrupt(digitalPinToPinChangeInterrupt(cut1)); detachPinChangeInterrupt(digitalPinToPinChangeInterrupt(cut2)); if(detonate) { explosion(); } else { if(disarmed) { safe(); } else { explosion(); } } waitkey(); delay_init(); delay_waste(200); }