/* 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 If the IP address of your shield is yourAddress: http://yourAddress/H turns the LED on http://yourAddress/L turns it off 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" //------------------------------------------------ typedef struct { char *key; uint8_t value; } record_type; record_type upperCaseAlphaChar[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 lowerCaseAlphaChar[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 numberChar[10] = { {"1", 0x0}, {"2", 0x1}, {"3", 0x2}, {"4", 0x3}, {"5", 0x4}, {"6", 0x5}, {"7", 0x6}, {"8", 0x7}, {"9", 0x8}, {"0", 0x9}}; record_type specialChar[8] = { {"-", 0x4B}, {".", 0x4D}, {",", 0x4E}, {"/", 0xA}, {"?", 0x26}, {"!", 0x27}, {"(", 0x33}, {")", 0x34}}; bool isLowercase = false; bool isNumber = false; //! Vielleicht smarter immer den vorherigen zu setzen und damit zu vergleichen bool wasLowercase = false; bool wasNumber = false; //------------------------------------------------ // Create AsyncWebServer object on port 80 AsyncWebServer server(80); // WiFiServer server(80); const char *PARAM_MESSAGE = "message"; void notFound(AsyncWebServerRequest *request) { request->send(404, "text/plain", "Not found"); } //------------------------------------------------ //-------------- SD-Card Functions --------------- void initSDCard() { if (!SD.begin()) { Serial.println("Card Mount Failed"); return; } uint8_t cardType = SD.cardType(); if (cardType == CARD_NONE) { Serial.println("No SD card attached"); return; } Serial.print("SD Card Type: "); if (cardType == CARD_MMC) { Serial.println("MMC"); } else if (cardType == CARD_SD) { Serial.println("SDSC"); } else if (cardType == CARD_SDHC) { Serial.println("SDHC"); } else { Serial.println("UNKNOWN"); } uint64_t cardSize = SD.cardSize() / (1024 * 1024); Serial.printf("SD Card Size: %lluMB\n", cardSize); } void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.printf("Listing directory: %s\n", dirname); File root = fs.open(dirname); if (!root) { Serial.println("Failed to open directory"); return; } if (!root.isDirectory()) { Serial.println("Not a directory"); return; } File file = root.openNextFile(); while (file) { if (file.isDirectory()) { Serial.print(" DIR : "); Serial.println(file.name()); if (levels) { listDir(fs, file.path(), levels - 1); } } else { Serial.print(" FILE: "); Serial.print(file.name()); Serial.print(" SIZE: "); Serial.println(file.size()); } file = root.openNextFile(); } } //------------------------------------------------ //--------------- 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!"); } } //! Ich brauche eine Funktion, die den vorherigen und den folgenden char prüft und checked, ob der lowercase, oder num ist und dann entsprechend den Modus umschaltet. //! Ich brauche eine Funktion, die den char mit einem IR command vergleicht und den IrSender.sendSony() ausführt. 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 nameSingleTrack(String titlestring) { Serial.print("track name: "); Serial.println(titlestring); //! Brauche ich den null terminator? 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 for (int i = 0; i < str_len; i++) // Loop over array { int s = char_array[i]; Serial.print("char: "); Serial.println(char_array[i]); //! Hier müssen verschiedene Sachen getestet werden: //! Ist das Array-Ende erreicht? Ist der case ein anderer als vom vorigen char? Prüft aktuelle char und entscheidet, welches Command gesendet wird //! Wie kann ich das in eigene Funktionen verpacken? // Test if char is a lowerCase letter if (isLowerCase(char_array[i])) { Serial.println("The character is lower case"); } } } 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 } //------------------------------------------------ //------------- Webserver Functions -------------- void initWiFi() { WiFi.mode(WIFI_STA); WiFi.begin(SSID, PASSWORD); Serial.print("Connecting to WiFi .."); while (WiFi.status() != WL_CONNECTED) { Serial.print('.'); delay(1000); } Serial.println(WiFi.localIP()); } //------------------------------------------------ //-------------------- Setup --------------------- void setup() { Serial.begin(115200); Serial.print("numberChar[1].value: "); Serial.println(numberChar[1].value); Serial.print("numberChar[1].key: "); Serial.println(numberChar[1].key); Serial.print("specialChar[0].value: "); Serial.println(specialChar[0].value); Serial.print("specialChar[0].key: "); Serial.println(specialChar[0].key); Serial.print("specialChar[2].value: "); Serial.println(specialChar[2].value); Serial.print("specialChar[7].value: "); Serial.println(specialChar[7].value); Serial.print("specialChar[7].key: "); Serial.println(specialChar[7].key); 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 message; if (request->hasParam("tracklist", true)) { message = request->getParam("tracklist", true)->value(); //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(); //Serial.println(message); nameSingleTrack(message); } 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"); }); // Send a POST request to /api/keyboard-input with a form field message set to server.on("/api/keyboard-input", HTTP_POST, [](AsyncWebServerRequest *request) { String message; if (request->hasParam("key", true)) { message = request->getParam("key", true)->value(); sendCommand20(message); } else { message = "No message sent"; } request->send(SD, "/index.html", "text/html"); }); server.onNotFound(notFound); server.begin(); } void loop() { }