// Panik Web Form Demo #define SERIAL_DEBUG #define ENABLE_UDP // define network constants #define MAC_DAVID { 0x90, 0xA2, 0xDA, 0x0D, 0xF4, 0x63 } #define MAC_PANIK { 0x90, 0xA2, 0xDA, 0x00, 0x9A, 0x94 } #define MAC_PANIK2 { 0x90, 0xA2, 0xDA, 0x0E, 0xF3, 0x77 } #define MAC_ADDRESS MAC_PANIK2 // include Arduino libraries #include #include #include #include #ifdef ENABLE_UDP #include #endif // include third party libraries #include "WebServer.h" // include in-house libs #include "PanikSwitch.h" // contains variable types for panik switch // define SD and Ethernet select ports #define SD_SELECT 4 #define ETHERNET_SELECT 10 // define machine variables #define DEBOUNCE_TRESHOLD 10 // 10 milliseconds #define ABORT_TRESHOLD 3000L // 3 seconds #define PULSE_TRESHOLD 50 // 50 milliseconds #define BLINK_TRESHOLD 100 // 100 milliseconds // output pins #define RELAY_BUTTON_LEDS 5 // Button LED indicator #define RELAY_GREEN_LEDS 6 // ``Non-Stop`` LED indicator #define RELAY_RED_LEDS 8 // ``Studio 1`` LED indicator #define RELAY_YELLOW_LEDS 7 // ``Studio 2`` LED indicator #define RELAY_NONSTOP_OR_STUD 4 // Relay : Non-stop or Studio #define RELAY_STUD1_OR_STUD2 9 // Relay : Studio 1 or Studio 2 // input pins #define BUTTON1 2 // ``Select`` button #define BUTTON2 3 // ``Confirm`` button #define NONSTOP_VIA_STUD1 14 // is nonstop coming out of studio 1 #define NONSTOP_VIA_STUD2 15 // is nonstop coming out of studio 2 /* define an array with led pins, indexes in that array correspond to the values of possible selections i.e.: RELAY_GREEN_LEDS: nonstop, RELAY_RED_LEDS: studio1, RELAY_YELLOW_LEDS: studio2 */ const int ledsArray[] = { RELAY_GREEN_LEDS, RELAY_RED_LEDS, RELAY_YELLOW_LEDS }; /* activeSelection is the variable that holds the active output of the switch it is declared as an attribute that keeps its value between arduino resets */ switchSelection_t activeSelection = 0; // __attribute__ ((section (".noinit"))); // variables and timers for blinking selection (selected but not confirmed) switchSelection_t blinkingSelection = nonstop; bool blinkingLedState = RELAY_STATE_CLOSED; unsigned long blinkingStartTime = 0, blinkingAbortTime = 0; // declaration of possible button states as enum type typedef enum { nochange, pressed, released } buttonEvent_t; // button event timers unsigned long button1LastEventTime = 0, button2LastEventTime = 0; // variables holding the state of each button // these are used during the debouncing process uint8_t button1State = LOW, button2State = LOW; // variables holding the non-stop status from studios 1 & 2 bool nonstop_via_stud1, nonstop_via_stud2; // declare functions buttonEvent_t debounce( const uint8_t buttonPin, uint8_t *buttonState, unsigned long *buttonLastEventTime ); static uint8_t mac[] = MAC_ADDRESS; #define PREFIX "" // instanciate web server WebServer webserver(PREFIX, 80); #ifdef ENABLE_UDP // and EthernetUDP instance to send notifications EthernetUDP Udp; IPAddress udp_remote_ip(192, 168, 17, 224); #endif #define NAMELEN 4 #define VALUELEN 4 typedef enum responseStatus { NO_POST, POST_OK, POST_ERROR }; inline void notify_udp() // notify over UDP { #ifdef ENABLE_UDP char str_selection[20]; snprintf(str_selection, 19, "{\"active\": %d}", activeSelection); #ifdef SERIAL_DEBUG Serial.println(F("Sending UDP... ")); Serial.println(str_selection); #endif Udp.beginPacket(udp_remote_ip, 1312); Udp.write(str_selection); Udp.endPacket(); #endif } // web resource void webCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) { server.httpSuccess("application/json"); if (type == WebServer::HEAD) return; responseStatus response_status = NO_POST; if (type == WebServer::POST) { char name[NAMELEN]; int name_len; char value[VALUELEN]; int value_len; response_status = POST_ERROR; while (server.readPOSTparam(name, NAMELEN, value, VALUELEN)) { if (strcmp(name, "s") == 0) { digitalWrite(ledsArray[activeSelection], RELAY_STATE_CLOSED); blinkingSelection = static_cast(atoi(value)); activeSelection = static_cast(atoi(value)); response_status = POST_OK; notify_udp(); } } } server.println(F("{")); server.print(" \"response\": "); server.print(response_status); server.print(",\n \"active\": "); server.print(activeSelection); server.print(",\n \"nonstop-via-stud1\": "); server.print(nonstop_via_stud1); server.print(",\n \"nonstop-via-stud2\": "); server.println(nonstop_via_stud2); server.println(F("}")); } // setup function void setup() { blinkingSelection = activeSelection = nonstop; // open serial communication for debugging Serial.begin(9600); Serial.println(F("Startup")); // disable SD and Ethernet ports before setup pinMode(SD_SELECT, OUTPUT); digitalWrite(SD_SELECT, HIGH); // disable SD card pinMode(ETHERNET_SELECT, OUTPUT); digitalWrite(ETHERNET_SELECT, HIGH); // disable Ethernet // start SPI (because Ethernet shield needs it) SPI.begin(); // start SPI // start Ethernet if (!(Ethernet.begin(mac) == 0)) // start network Serial.println(Ethernet.localIP()); else { #ifdef SERIAL_DEBUG Serial.println(F("Network Error")); #endif while (1) ; } #ifdef ENABLE_UDP if (! Udp.begin(1312)) { #ifdef SERIAL_DEBUG Serial.println(F("Failed to initiate UDP")); #endif } #endif // set mode for used pins pinMode(RELAY_RED_LEDS, OUTPUT); digitalWrite(RELAY_RED_LEDS, RELAY_STATE_CLOSED); pinMode(RELAY_YELLOW_LEDS, OUTPUT); digitalWrite(RELAY_YELLOW_LEDS, RELAY_STATE_CLOSED); pinMode(RELAY_GREEN_LEDS, OUTPUT); digitalWrite(RELAY_GREEN_LEDS, RELAY_STATE_CLOSED); pinMode(RELAY_BUTTON_LEDS, OUTPUT); digitalWrite(RELAY_BUTTON_LEDS, RELAY_STATE_CLOSED); pinMode(RELAY_NONSTOP_OR_STUD, OUTPUT); digitalWrite(RELAY_NONSTOP_OR_STUD, RELAY_STATE_CLOSED); pinMode(RELAY_STUD1_OR_STUD2, OUTPUT); digitalWrite(RELAY_STUD1_OR_STUD2, RELAY_STATE_CLOSED); pinMode(BUTTON1, INPUT); pinMode(BUTTON2, INPUT); pinMode(NONSTOP_VIA_STUD1, INPUT); pinMode(NONSTOP_VIA_STUD2, INPUT); // configure web server pages webserver.setDefaultCommand(&webCmd); // start web server webserver.begin(); } // loop function void loop() { // update variables indicating if non-stop is selected in studios 1&2 nonstop_via_stud1 = digitalRead(NONSTOP_VIA_STUD1); if (nonstop_via_stud1 == 0) nonstop_via_stud1 = 1; else nonstop_via_stud1 = 0; nonstop_via_stud2 = digitalRead(NONSTOP_VIA_STUD2); if (nonstop_via_stud2 == 0) nonstop_via_stud2 = 1; else nonstop_via_stud2 = 0; // check if we have a HTTP request (and respond) webserver.processConnection(); // handle button 1 (select button) switch ( debounce(BUTTON1, &button1State, &button1LastEventTime) ) { case nochange: break; case pressed: #ifdef SERIAL_DEBUG Serial.println(F("Button 1 pressed")); #endif digitalWrite(ledsArray[blinkingSelection], RELAY_STATE_CLOSED); blinkingSelection++; blinkingStartTime = millis(), blinkingAbortTime = millis(); blinkingLedState = RELAY_STATE_OPEN; break; case released: #ifdef SERIAL_DEBUG Serial.println(F("Button 1 released")); #endif break; } // handle button 2 (confirm button) switch ( debounce(BUTTON2, &button2State, &button2LastEventTime) ) { case nochange: break; case pressed: #ifdef SERIAL_DEBUG Serial.println(F("Button 2 pressed")); #endif blinkingAbortTime = 0; // disable blinking auto abort break; case released: #ifdef SERIAL_DEBUG Serial.println(F("Button 2 released")); #endif if (activeSelection != blinkingSelection) { digitalWrite(ledsArray[activeSelection], RELAY_STATE_CLOSED); activeSelection = blinkingSelection; // relay states must be changed now #ifdef SERIAL_DEBUG Serial.print(F("Active Selection: ")); Serial.println(activeSelection); #endif notify_udp(); break; } } // handle auto abort for blinking led (if any) if ( (blinkingAbortTime != 0) && ((millis() - blinkingAbortTime) >= ABORT_TRESHOLD) ) { digitalWrite(ledsArray[blinkingSelection], RELAY_STATE_CLOSED); blinkingAbortTime = 0; blinkingSelection = activeSelection; } // handle blinking led (if selected is not current state) if (activeSelection != blinkingSelection) { if ((millis() - blinkingStartTime) >= BLINK_TRESHOLD) { digitalWrite(ledsArray[blinkingSelection], blinkingLedState); digitalWrite(RELAY_BUTTON_LEDS, !blinkingLedState); blinkingStartTime = millis(); blinkingLedState = !blinkingLedState; } } else { digitalWrite(RELAY_BUTTON_LEDS, RELAY_STATE_CLOSED); } digitalWrite(ledsArray[activeSelection], RELAY_STATE_OPEN); // update audio channel relays if (activeSelection == nonstop) { digitalWrite(RELAY_NONSTOP_OR_STUD, RELAY_STATE_CLOSED); digitalWrite(RELAY_STUD1_OR_STUD2, RELAY_STATE_CLOSED); } else if (activeSelection == studio1) { digitalWrite(RELAY_NONSTOP_OR_STUD, RELAY_STATE_OPEN); digitalWrite(RELAY_STUD1_OR_STUD2, RELAY_STATE_CLOSED); } else if (activeSelection == studio2) { digitalWrite(RELAY_NONSTOP_OR_STUD, RELAY_STATE_OPEN); digitalWrite(RELAY_STUD1_OR_STUD2, RELAY_STATE_OPEN); } } buttonEvent_t debounce( const uint8_t buttonPin, uint8_t *buttonState, unsigned long *buttonLastEventTime ) { uint8_t buttonLastState = *buttonState; uint8_t buttonReading = digitalRead(buttonPin); if ( (*buttonLastEventTime == 0) && (*buttonState != buttonReading) ) { // something happened, start the debouncing process *buttonLastEventTime = millis(); } if (*buttonLastEventTime != 0) { long buttonEventTimer = millis() - *buttonLastEventTime; if ( (buttonEventTimer < DEBOUNCE_TRESHOLD) && (*buttonState == buttonReading) ) { // noise or bouncing, abort *buttonLastEventTime = 0; } if ( (buttonEventTimer >= DEBOUNCE_TRESHOLD) && (buttonReading != *buttonState) ) { // new button state was maintained *buttonLastEventTime = 0; *buttonState = buttonReading; } } // debouncing finished, return button state if (*buttonLastEventTime == 0) { // comparing buttonState and buttonLastState tells whether the // button is pressed or released if ( (*buttonState == HIGH) && (buttonLastState == LOW) ) { // button just pressed buttonLastState = *buttonState; return pressed; } if ( (*buttonState == LOW) && (buttonLastState == HIGH) ) { // button just released buttonLastState = *buttonState; return released; } } return nochange; }