/* WebServer to use as replacement for Sony RM-D27M to control Sony MiniDisc decks. Based on the the following sketches: - SimpleWifiServer example - ESP32/SD_Test - IRremote/SendDemo Circuit: - LED attached to pin 13 (optional) - IR Transmitter attached to pin 4 (connect to 5v or 3v3) - SD-Module to load HTML and CSS from SD-card Connect the SD card to the following pins: SD Card | ESP32 ------- | ----- 3v3 | 3.3V CS | 5 MOSI | 23 CLK | 18 MISO | 19 GND | GND developed for the ESP32vn IoT Uni board 2024 */ //----------------------------------------- // Arduino Web Server using AJAX // HTML code of webpage is stored on SD card //----------------------------------------- #include #include #include #include #include "FS.h" #include "SD.h" #include "SPI.h" #include #include "secrets.h" #include "defines.h" #include "initfunctions.h" //------------------------------------------------ typedef struct { char key; uint8_t value; } record_type; record_type upperCaseAlphaChars[26] = { {'A', 0x1D}, {'B', 0x1E}, {'C', 0x1F}, {'D', 0x1B}, {'E', 0x1A}, {'F', 0x0}, {'G', 0x1}, {'H', 0x2}, {'I', 0x3}, {'J', 0x4}, {'K', 0x5}, {'L', 0x6}, {'M', 0x7}, {'N', 0x8}, {'O', 0x9}, {'P', 0x40}, {'Q', 0x41}, {'R', 0x42}, {'S', 0x43}, {'T', 0x44}, {'U', 0x45}, {'V', 0x46}, {'W', 0x47}, {'X', 0x48}, {'Y', 0x49}, {'Z', 0x4A}}; record_type lowerCaseAlphaChars[26] = { {'a', 0x1D}, {'b', 0x1E}, {'c', 0x1F}, {'d', 0x1B}, {'e', 0x1A}, {'f', 0x0}, {'g', 0x1}, {'h', 0x2}, {'i', 0x3}, {'j', 0x4}, {'k', 0x5}, {'l', 0x6}, {'m', 0x7}, {'n', 0x8}, {'o', 0x9}, {'p', 0x40}, {'q', 0x41}, {'r', 0x42}, {'s', 0x43}, {'t', 0x44}, {'u', 0x45}, {'v', 0x46}, {'w', 0x47}, {'x', 0x48}, {'y', 0x49}, {'z', 0x4A}}; record_type numberChars[10] = { {'1', 0x0}, {'2', 0x1}, {'3', 0x2}, {'4', 0x3}, {'5', 0x4}, {'6', 0x5}, {'7', 0x6}, {'8', 0x7}, {'9', 0x8}, {'0', 0x9}}; record_type specialChars[8] = { {'-', 0x4B}, {'.', 0x4D}, {',', 0x4E}, {'/', 0xA}, {'?', 0x26}, {'!', 0x27}, {'(', 0x33}, {')', 0x34}}; bool beginTask = false; bool taskIsRunning = false; bool wasLowercase = false; bool wasNumber = false; int delayAmount = 1000; String titleString = ""; //------------------------------------------------ // Create AsyncWebServer object on port 80 AsyncWebServer server(80); void notFound(AsyncWebServerRequest *request) { request->send(404, "text/plain", "Not found"); } //------------------------------------------------ //------------- Helper Functions ----------------- void triggerDelay() { delay(delayAmount); } //------------------------------------------------ //--------------- IR Functions ------------------- void initIR() { IrSender.begin(IR_SEND_PIN, ENABLE_LED_FEEDBACK, USE_DEFAULT_FEEDBACK_LED_PIN); // Specify send pin and enable feedback LED at default feedback LED pin Serial.println(); Serial.print(F("Send IR signals at pin ")); Serial.println(IR_SEND_PIN); } void sendButton12(int command) { Serial.print("sendButton12 command: "); Serial.println(command); Serial.flush(); IrSender.sendSony(0xF, command, 2, 12); } void sendButton20(int command) { Serial.print(F("Send Sony/SIRCS with 7 command and 13 address bits: ")); Serial.println(command); Serial.flush(); IrSender.sendSony(0xF & 0x1FFF, command & 0x7F, 2, SIRCS_20_PROTOCOL); } void sendCommand12(String commandString) { if (commandString == "POWER") sendButton12(POWER); if (commandString == "PLAY") sendButton12(PLAY); if (commandString == "PAUSE") sendButton12(PAUSE); if (commandString == "STOP") sendButton12(STOP); if (commandString == "PREV") sendButton12(PREV); if (commandString == "NEXT") sendButton12(NEXT); else { Serial.println("wrong command!"); } } void sendCommand20(String commandString) { if (commandString == "Q") sendButton12(keyQ); if (commandString == "W") sendButton12(keyW); if (commandString == "E") sendButton12(keyE); if (commandString == "R") sendButton12(keyR); if (commandString == "T") sendButton12(keyT); if (commandString == "Z") sendButton12(keyZ); else { Serial.println("wrong command!"); } } void caseToggleLow() { if (wasNumber == true) { Serial.println("toggle to char"); wasNumber = false; IrSender.sendSony(0xF, 0x37, 2, 12); triggerDelay(); if (wasLowercase == false) { Serial.println("toggle case low"); IrSender.sendSony(0xF, 0x36, 2, 12); triggerDelay(); } if (wasLowercase == true) Serial.println("no toggle"); } if (wasLowercase == false) { Serial.println("toggle case low"); IrSender.sendSony(0xF, 0x36, 2, 12); triggerDelay(); } if (wasLowercase == true) Serial.println("no toggle"); } void caseToggleUp() { if (wasNumber == true) { Serial.println("toggle to char"); wasNumber = false; if (wasLowercase == true) { Serial.println("toggle case up"); IrSender.sendSony(0xF, 0x36, 2, 12); triggerDelay(); } if (wasLowercase == false) Serial.println("no toggle"); } if (wasLowercase == true) { Serial.println("toggle case up"); IrSender.sendSony(0xF, 0x36, 2, 12); triggerDelay(); } if (wasLowercase == false) Serial.println("no toggle"); } void toggleDigit() { if (wasNumber == true) Serial.println("no toggle"); if (wasNumber == false) { Serial.println("toggle to digit"); wasNumber = true; IrSender.sendSony(0xF, 0x37, 2, 12); triggerDelay(); } } void sendSpace() { IrSender.sendSony(0xF, 0x4C, 2, 12); triggerDelay(); } void sendUpperCaseChar(char uppercaseCharString) { for (int x = 0; x <= sizeof(upperCaseAlphaChars) / sizeof(upperCaseAlphaChars[0]); x++) { if (upperCaseAlphaChars[x].key == uppercaseCharString) { IrSender.sendSony(0xF, upperCaseAlphaChars[x].value, 2, 12); triggerDelay(); } } } void sendLowerCaseChar(char lowercaseCharString) { for (int x = 0; x <= sizeof(lowerCaseAlphaChars) / sizeof(lowerCaseAlphaChars[0]); x++) { if (lowerCaseAlphaChars[x].key == lowercaseCharString) { IrSender.sendSony(0xF, lowerCaseAlphaChars[x].value, 2, 12); triggerDelay(); } } } void sendDigitChar(char digitCharString) { for (int x = 0; x <= sizeof(numberChars) / sizeof(numberChars[0]); x++) { if (numberChars[x].key == digitCharString) { IrSender.sendSony(0xF, numberChars[x].value, 2, 12); triggerDelay(); } } } void sendSpecialChar(char specialCharString) { for (int x = 0; x <= sizeof(specialChars) / sizeof(specialChars[0]); x++) { if (specialChars[x].key == specialCharString) { /* Serial.println("specialChar value: "); Serial.println(specialChars[x].value); */ IrSender.sendSony(0xF, specialChars[x].value, 2, 12); triggerDelay(); } } } void beginEditing() { Serial.println("Begin editing"); IrSender.sendSony(0xF, 0x3C, 2, 12); // Send "name" command triggerDelay(); } void finalizeTrack() { Serial.println("Finalize track"); IrSender.sendSony(0xF, 0x3C, 2, 12); // Send "name" command wasLowercase = false; wasNumber = false; } void nameSingleTrack(String titlestring) { Serial.print("track name: "); Serial.println(titlestring); int str_len = titlestring.length() + 1; // Length (with one extra character for the null terminator) Serial.print("string length: "); Serial.println(str_len); char char_array[str_len]; // Prepare the character array (the buffer) titlestring.toCharArray(char_array, str_len); // Copy it over beginEditing(); for (int i = 0; i < str_len; i++) // Loop over array { char c = char_array[i]; if (i == str_len - 1) // array length minus null terminator { Serial.println("End of line."); finalizeTrack(); } else { Serial.print("index: "); Serial.println(i); Serial.print("char: "); Serial.println(c); if (isLowerCase(c)) // Test if char is a lowerCase letter { Serial.println("The character is lower case"); caseToggleLow(); wasLowercase = true; wasNumber = false; sendLowerCaseChar(c); } if (isUpperCase(c)) // Test if char is a upperCase letter { Serial.println("The character is upper case"); caseToggleUp(); wasLowercase = false; wasNumber = false; sendUpperCaseChar(c); } if (isDigit(c)) // Test if char is a digit { Serial.println("The character is digit"); toggleDigit(); wasNumber = true; sendDigitChar(c); } if (isPunct(c)) // Test if char is special char { Serial.println("The character is special char"); sendSpecialChar(c); } if (isSpace(c)) // Test if char is a space { Serial.println("The character is a space"); sendSpace(); } } } } void nameAllTracks(String message) { Serial.print("track list: "); Serial.println(message); // split into array of strings after each line break // use nameSingleTrack with each track } void triggerNewTrackNaming() { Serial.print("START TITLING"); if (titleString == "") { Serial.print("titleString: "); Serial.println(titleString); } if (titleString != "") { Serial.print("new titleString: "); Serial.println(titleString); nameSingleTrack(titleString); titleString = ""; } Serial.print("END TITLING"); } //------------------------------------------------ //-------------------- Setup --------------------- void setup() { //------------------------------------------------ Serial.begin(115200); initWiFi(); initSDCard(); pinMode(LED_PIN, OUTPUT); // set the LED pin mode initIR(); server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SD, "/index.html", "text/html"); }); server.serveStatic("/", SD, "/"); // Send a POST request to /api/control-led with a form field message set to server.on("/api/control-led", HTTP_POST, [](AsyncWebServerRequest *request) { String message; if (request->hasParam("led", true)) { message = request->getParam("led", true)->value(); if (message == "1") digitalWrite(LED_PIN, HIGH); if (message == "0") digitalWrite(LED_PIN, LOW); } else { message = "No message sent"; } request->send(SD, "/index.html", "text/html"); }); // Send a POST request to /api/gettext with a form field message set to server.on("/api/gettracklist", HTTP_POST, [](AsyncWebServerRequest *request) { String title; String message; if (request->hasParam("albumtitle", true)) { title = request->getParam("albumtitle", true)->value(); Serial.print("album title: "); Serial.println(title); /* nameAllTracks(message); */ } else { title = "No message sent"; } if (request->hasParam("tracklist", true)) { message = request->getParam("tracklist", true)->value(); Serial.print("tracklist: "); Serial.println(message); /* nameAllTracks(message); */ } else { message = "No message sent"; } request->send(SD, "/index.html", "text/html"); }); // Send a GET request to /api/gettextinput?message= server.on("/api/gettrack", HTTP_GET, [](AsyncWebServerRequest *request) { String message; if (request->hasParam("singletrack")) { message = request->getParam("singletrack")->value(); titleString = message; beginTask = true; } else { message = "No message sent"; } request->send(SD, "/index.html", "text/html"); }); // Send a POST request to /api/control-deck with a form field message set to server.on("/api/control-deck", HTTP_POST, [](AsyncWebServerRequest *request) { String message; if (request->hasParam("control", true)) { message = request->getParam("control", true)->value(); sendCommand12(message); } else { message = "No message sent"; } request->send(SD, "/index.html", "text/html"); }); server.onNotFound(notFound); server.begin(); } void loop() { if (beginTask) { taskIsRunning = true; triggerNewTrackNaming(); taskIsRunning = false; Serial.println("work has finished"); beginTask = false; } }