553 lines
13 KiB
Arduino
553 lines
13 KiB
Arduino
/*
|
|
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 <Arduino.h>
|
|
#include <WiFi.h>
|
|
#include <AsyncTCP.h>
|
|
#include <ESPAsyncWebServer.h>
|
|
#include "FS.h"
|
|
#include "SD.h"
|
|
#include "SPI.h"
|
|
#include <IRremote.hpp>
|
|
#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 <IP>/api/control-led with a form field message set to <message>
|
|
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 <IP>/api/gettext with a form field message set to <message>
|
|
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 <IP>/api/gettextinput?message=<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 <IP>/api/control-deck with a form field message set to <message>
|
|
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 <IP>/api/keyboard-input with a form field message set to <message>
|
|
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()
|
|
{
|
|
if (beginTask)
|
|
{
|
|
taskIsRunning = true;
|
|
triggerNewTrackNaming();
|
|
taskIsRunning = false;
|
|
Serial.println("work has finished");
|
|
beginTask = false;
|
|
}
|
|
}
|