#include <WiFi.h>
#include <WebServer.h>
#include <IRremote.hpp>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Preferences.h>
#include <vector>
#define IR_RECEIVE_PIN 14
#define IR_SEND_PIN 15
#define TFT_CS 15
#define TFT_RST 4
#define TFT_DC 2
const char* ssid = "Wokwi-GUEST";
const char* password = "";
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
WebServer server(80);
Preferences preferences;
std::vector<uint32_t> irCodeMap[10]; // Use a vector array to map digits to IR codes
class SongRequest {
public:
String song;
String customerId;
SongRequest(String s, String c) : song(s), customerId(c) {}
};
std::vector<SongRequest> songQueue;
std::vector<String> customers;
char currentNumber = '0';
bool recordMode = true;
bool playMode = false;
unsigned long interval = 0; // 自动发送的间隔时间,单位为毫秒
unsigned long previousMillis = 0; // 上一次发送的时间
// HTML content for admin and customer pages
const char ADMIN_PAGE[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<title>Admin Control</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial, sans-serif; text-align: center; }
h1 { color: #333; }
p { font-size: 1.2em; }
a { display: inline-block; padding: 10px 20px; margin: 10px; border: 1px solid #333; color: #333; text-decoration: none; border-radius: 4px; }
a:hover { background-color: #333; color: #fff; }
input { padding: 10px; font-size: 1em; }
</style>
</head>
<body>
<h1>Admin Control</h1>
<p>Current Mode: %MODE%</p>
<p>Current Interval: %INTERVAL% seconds</p>
<a href="/toggle">Toggle Mode</a><br><br>
<h2>Song Queue</h2>
<ul>
%SONG_QUEUE%
</ul>
<h2>Set Auto Send Interval</h2>
<form action="/setInterval" method="POST">
<label for="interval">Interval (ms):</label>
<input type="text" id="interval" name="interval"><br><br>
<input type="submit" value="Set Interval">
</form>
<h2>Add Customer</h2>
<form action="/addCustomer" method="POST">
<label for="customer">Customer ID:</label>
<input type="text" id="customer" name="customer"><br><br>
<input type="submit" value="Add Customer">
</form>
</body>
</html>
)rawliteral";
const char CUSTOMER_PAGE[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<title>Customer Page</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial, sans-serif; text-align: center; }
h1 { color: #333; }
p { font-size: 1.2em; }
img { width: 200px; height: 200px; }
</style>
</head>
<body>
<h1>Welcome, Customer %CUSTOMER_ID%</h1>
<p>Scan this QR code to submit your song:</p>
<img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=%URL%">
<p>GO to site:</p>
<a href="%URL%">Submit your song</a>
</body>
</html>
)rawliteral";
const char CUSTOMER_SUBMIT_PAGE[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<title>Submit Song</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial, sans-serif; text-align: center; }
h1 { color: #333; }
form { margin: 20px auto; }
input[type="text"] { padding: 10px; font-size: 1.2em; }
input[type="submit"] { padding: 10px 20px; font-size: 1.2em; }
</style>
</head>
<body>
<h1>Submit Song</h1>
<form action="/submitSong" method="POST">
<label for="song">Song Number:</label>
<input type="text" id="song" name="song"><br><br>
<input type="hidden" id="customer" name="customer" value="%CUSTOMER_ID%">
<input type="submit" value="Submit">
</form>
</body>
</html>
)rawliteral";
// Function declarations
void handleAdmin();
void handleCustomer();
void handleAddCustomer();
void handleToggle();
void handleSubmit();
void handleSubmitSong();
void handleSetInterval();
void displayCurrentNumber();
void displayMapping(char number, uint32_t code);
void sendIRCode(String song);
void blinkLED(int pin, int times);
void displayQRCode(String url);
void loadPreferences();
void savePreferences();
bool allNumbersMapped();
void updateAdminPage();
void updateTFTScreen();
void handleAdmin() {
String html = FPSTR(ADMIN_PAGE);
html.replace("%MODE%", playMode ? "Play Mode" : "Record Mode");
String songList = "";
for (const auto& song : songQueue) {
songList += "<li>Song: " + song.song + " (Customer: " + song.customerId + ")</li>";
}
html.replace("%SONG_QUEUE%", songList);
html.replace("%INTERVAL%", String(interval / 1000)); // 以秒为单位显示当前间隔时间
server.send(200, "text/html", html);
}
void updateAdminPage() {
String html = FPSTR(ADMIN_PAGE);
html.replace("%MODE%", playMode ? "Play Mode" : "Record Mode");
String songList = "";
for (const auto& song : songQueue) {
songList += "<li>Song: " + song.song + " (Customer: " + song.customerId + ")</li>";
}
html.replace("%SONG_QUEUE%", songList);
html.replace("%INTERVAL%", String(interval / 1000)); // 以秒为单位显示当前间隔时间
server.sendContent(html);
}
void updateTFTScreen() {
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(0, 0);
tft.setTextSize(2);
if (songQueue.empty()) {
tft.print("No songs in queue");
} else {
for (const auto& song : songQueue) {
tft.print("Song: " + song.song + " from " + song.customerId);
tft.setCursor(0, tft.getCursorY() + 20);
}
}
}
void handleCustomer() {
if (server.hasArg("customer")) {
String customerId = server.arg("customer");
String html = FPSTR(CUSTOMER_PAGE);
html.replace("%CUSTOMER_ID%", customerId);
String url = "http://localhost:9080/submit?customer=" + customerId;
html.replace("%URL%", url);
String link = "<a href=\"" + url + "\">Submit your song</a>";
html.replace("%LINK%", link);
server.send(200, "text/html", html);
displayQRCode(url);
} else {
server.send(400, "text/html", "<html><body><h1>Error: No customer ID provided</h1><a href='/admin'>Back</a></body></html>");
}
}
void handleAddCustomer() {
if (server.hasArg("customer")) {
String customerId = server.arg("customer");
customers.push_back(customerId);
handleCustomer();
} else {
server.send(400, "text/html", "<html><body><h1>Error: No customer ID provided</h1><a href='/admin'>Back</a></body></html>");
}
}
void handleToggle() {
playMode = !playMode;
handleAdmin();
if (playMode) {
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(0, 0);
tft.setTextSize(2);
tft.print("Play mode on");
} else {
displayCurrentNumber();
}
}
void handleSubmit() {
if (server.hasArg("customer")) {
String customerId = server.arg("customer");
String html = FPSTR(CUSTOMER_SUBMIT_PAGE);
html.replace("%CUSTOMER_ID%", customerId);
server.send(200, "text/html", html);
} else {
server.send(400, "text/html", "<html><body><h1>Error: No customer ID provided</h1><a href='/admin'>Back</a></body></html>");
}
}
void handleSubmitSong() {
if (server.hasArg("song") && server.hasArg("customer")) {
String song = server.arg("song");
String customerId = server.arg("customer");
// 检查歌曲号码是否为六位数
if (song.length() != 6 || !song.toInt()) {
server.send(400, "text/html", "<html><body><h1>Error: Song number must be a six-digit number</h1><a href='/submit?customer=" + customerId + "'>Back</a></body></html>");
return;
}
songQueue.push_back(SongRequest(song, customerId));
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(0, 0);
tft.setTextSize(2);
tft.print("Song queued: ");
tft.print(song);
tft.setCursor(0, 20);
tft.print("Customer: ");
tft.print(customerId);
tft.setCursor(0, 40);
for (const auto& songs : songQueue) {
tft.print("Song: " + songs.song + " from " + songs.customerId);
tft.setCursor(0, tft.getCursorY() + 20);
}
savePreferences();
server.send(200, "text/html", "<html><body><h1>Song Queued!</h1><a href='/submit?customer=" + customerId + "'>Back</a></body></html>");
} else {
server.send(400, "text/html", "<html><body><h1>Error: No song number provided</h1><a href='/customer?customer=" + server.arg("customer") + "'>Back</a></body></html>");
}
}
void handleSetInterval() {
if (server.hasArg("interval")) {
interval = server.arg("interval").toInt();
server.send(200, "text/html", "<html><body><h1>Interval Set!</h1><a href='/admin'>Back to Admin</a></body></html>");
} else {
server.send(400, "text/html", "<html><body><h1>Error: No interval provided</h1><a href='/admin'>Back to Admin</a></body></html>");
}
}
void setup() {
Serial.begin(115200);
Serial.println("Initializing IR Receiver...");
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
Serial.println("IR Receiver initialized.");
tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
displayCurrentNumber();
Serial.println("ILI9341 initialized.");
pinMode(IR_SEND_PIN, OUTPUT);
digitalWrite(IR_SEND_PIN, LOW);
Serial.print("Connecting to WiFi");
WiFi.begin(ssid, password);
int attempt = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(100);
Serial.print(".");
attempt++;
if (attempt > 50) {
Serial.println("\nFailed to connect to WiFi. Restarting...");
ESP.restart();
}
}
Serial.println(" Connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
preferences.begin("songQueue", false);
loadPreferences();
server.on("/", handleAdmin);
server.on("/admin", handleAdmin);
server.on("/toggle", handleToggle);
server.on("/addCustomer", HTTP_POST, handleAddCustomer);
server.on("/customer", handleCustomer);
server.on("/submit", handleSubmit);
server.on("/submitSong", HTTP_POST, handleSubmitSong);
server.on("/setInterval", HTTP_POST, handleSetInterval);
server.begin();
Serial.println("Web server started");
}
void loop() {
server.handleClient();
if (!playMode && IrReceiver.decode()) {
Serial.println("IR signal received.");
uint32_t irCode = IrReceiver.decodedIRData.decodedRawData;
irCodeMap[currentNumber - '0'].push_back(irCode); // Store IR code in the appropriate vector
Serial.print("Recorded ");
Serial.print(currentNumber);
Serial.print(" for code ");
Serial.println(irCode, HEX);
displayMapping(currentNumber, irCode);
IrReceiver.resume();
}
if (playMode && !songQueue.empty()) {
unsigned long currentMillis = millis();
if (interval > 0 && currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
String song = songQueue.front().song;
String customerId = songQueue.front().customerId;
sendIRCode(song);
songQueue.erase(songQueue.begin()); // 删除队列中的第一个元素
savePreferences(); // 保存首选项
Serial.print("Sent and deleted song number: ");
Serial.print(song);
Serial.print(" from customer ID: ");
Serial.println(customerId);
updateAdminPage(); // 更新Admin页面
updateTFTScreen(); // 更新TFT屏幕
}
}
}
void displayCurrentNumber() {
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(0, 0);
tft.setTextSize(2);
tft.print("Mapping signal to:");
tft.setCursor(0, 40);
tft.setTextSize(4);
tft.print(currentNumber);
Serial.print("Current number to map: ");
Serial.println(currentNumber);
}
void displayMapping(char number, uint32_t code) {
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(0, 0);
tft.setTextSize(2);
tft.print("Mapped ");
tft.print(number);
tft.print(" to code:");
tft.setCursor(0, 40);
tft.setTextSize(4);
tft.print(code, HEX);
delay(2000);
// 检查所有数字是否完成输入
if (allNumbersMapped()) {
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(0, 0);
tft.setTextSize(4);
tft.print("Finish");
} else {
currentNumber = (currentNumber == '9') ? '0' : currentNumber + 1;
displayCurrentNumber();
}
}
bool allNumbersMapped() {
for (int i = 0; i < 10; i++) {
if (irCodeMap[i].empty()) {
return false;
}
}
return true;
}
void sendIRCode(String song) {
for (char digit : song) {
uint32_t irCode = 0;
for (uint32_t code : irCodeMap[digit - '0']) {
irCode = code;
break;
}
if (irCode != 0) {
blinkLED(IR_SEND_PIN, 32);
delay(500);
}
}
}
void blinkLED(int pin, int times) {
for (int i = 0; i < times; i++) {
digitalWrite(pin, HIGH);
delayMicroseconds(560);
digitalWrite(pin, LOW);
delayMicroseconds(560);
}
}
void displayQRCode(String url) {
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(0, 0);
tft.setTextSize(2);
tft.print("Scan QR Code:");
tft.setCursor(0, 40);
tft.setTextSize(2);
tft.print(url);
}
void loadPreferences() {
int size = preferences.getInt("queue_size", 0);
songQueue.clear();
for (int i = 0; i < size; i++) {
String songKey = "song_" + String(i);
String customerKey = "customer_" + String(i);
String song = preferences.getString(songKey.c_str(), "");
String customer = preferences.getString(customerKey.c_str(), "");
if (song.length() > 0 && customer.length() > 0) {
songQueue.push_back(SongRequest(song, customer));
}
}
}
void savePreferences() {
preferences.putInt("queue_size", songQueue.size());
for (int i = 0; i < songQueue.size(); i++) {
String songKey = "song_" + String(i);
String customerKey = "customer_" + String(i);
preferences.putString(songKey.c_str(), songQueue[i].song);
preferences.putString(customerKey.c_str(), songQueue[i].customerId);
}
}