#include #include #include #include "structs.h" #include "upload_page.h" #include "prototypes.h" const char* ssid = "PPIA"; const char* password = "pawelpdaldonejta"; unsigned char brightness = 100; WebServer server(80); Pixel new_image[16][16]; char* fileContent = nullptr; size_t fileContent_len = 0; String upload_error_message = ""; void handleBrightness() { if (server.hasArg("value")) { brightness = server.arg("value").toInt(); } server.send(200, "text/plain", "OK"); } void handleRoot() { server.send(200, "text/html", index_html); } void handleUploadPage() { server.send(200, "text/html", upload_page_html); } void handleBmpUpload() { HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { upload_error_message = ""; if (fileContent) { free(fileContent); fileContent = nullptr; fileContent_len = 0; } } else if (upload.status == UPLOAD_FILE_WRITE) { if (upload.name == "image") { fileContent = (char*)realloc(fileContent, fileContent_len + upload.currentSize); memcpy(fileContent + fileContent_len, upload.buf, upload.currentSize); fileContent_len += upload.currentSize; } } else if (upload.status == UPLOAD_FILE_END) { if (upload.name == "image") { // Read BMP header char header[54]; memcpy(header, fileContent, 54); int dataOffset = *(int*)&header[10]; int width = *(int*)&header[18]; int height = *(int*)&header[22]; short bitsPerPixel = *(short*)&header[28]; if (bitsPerPixel != 24) { upload_error_message = "Unsupported BMP format: Only 24-bit BMPs are supported"; return; } if (width < 1 || width > 48 || height < 1 || height > 16) { upload_error_message = "Invalid image dimensions"; return; } saved_images[0].width = width; saved_images[0].height = height; // Read pixel data int row_padded = (width*3 + 3) & (~3); char* pixel_data = fileContent + dataOffset; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int bmp_row = height - 1 - y; saved_images[0].pixels[y][x].b = pixel_data[bmp_row*row_padded + x*3 + 0]; saved_images[0].pixels[y][x].g = pixel_data[bmp_row*row_padded + x*3 + 1]; saved_images[0].pixels[y][x].r = pixel_data[bmp_row*row_padded + x*3 + 2]; } } free(fileContent); fileContent = nullptr; fileContent_len = 0; } } } void handleText() { if (server.hasArg("text") && server.hasArg("color") && server.hasArg("slowness")) { String text_str = server.arg("text"); String colorStr = server.arg("color"); unsigned char slowness = server.arg("slowness").toInt(); short disappear_time = server.hasArg("disappear") ? server.arg("disappear").toInt() : -1; bool is_small = true; if (server.hasArg("fontSize") && server.arg("fontSize") != "") { if (server.arg("fontSize") == "medium") { is_small = false; } } bool is_repeating = false; if (server.hasArg("is_repeating")) { is_repeating = server.arg("is_repeating") == "true"; } char text[TEXT_MAX_LENGTH + 1]; text_str.toCharArray(text, TEXT_MAX_LENGTH + 1); uint32_t color = strtol(colorStr.substring(1).c_str(), NULL, 16); if (server.hasArg("position")) { String position = server.arg("position"); if (position == "top") { addNewTextNode(text, color, false, 43, 0, slowness, true, is_small, disappear_time, is_repeating); } else if (position == "bottom") { addNewTextNode(text, color, false, -text_str.length() * 6, 9, slowness, true, is_small, disappear_time, is_repeating); } server.send(200, "text/plain", "OK"); } else { short x = 0; short y = 0; if (server.hasArg("x") && server.arg("x") != "") { x = server.arg("x").toInt(); } if (server.hasArg("y") && server.arg("y") != "") { y = server.arg("y").toInt(); } addNewTextNode(text, color, false, x, y, slowness, true, is_small, disappear_time, is_repeating); server.send(200, "text/plain", "OK"); } } else { server.send(400, "text/plain", "Invalid arguments for /text"); } } void handleMulticolorText() { if (server.hasArg("text") && server.hasArg("colors") && server.hasArg("slowness")) { String text_str = server.arg("text"); String colors_str = server.arg("colors"); unsigned char slowness = server.arg("slowness").toInt(); short disappear_time = server.hasArg("disappear") ? server.arg("disappear").toInt() : -1; bool is_small = true; if (server.hasArg("fontSize") && server.arg("fontSize") != "") { if (server.arg("fontSize") == "medium") { is_small = false; } } bool is_repeating = false; if (server.hasArg("is_repeating")) { is_repeating = server.arg("is_repeating") == "true"; } char text[TEXT_MAX_LENGTH + 1]; text_str.toCharArray(text, TEXT_MAX_LENGTH + 1); RGBWithIndex colors[4]; int color_count = 0; String color_part = ""; int last_comma = -1; for (int i = 0; i < colors_str.length() && color_count < 4; i++) { if (colors_str.charAt(i) == ',') { color_part = colors_str.substring(last_comma + 1, i); last_comma = i; if (color_part.length() > 0) { long color_val = strtol(color_part.substring(1).c_str(), NULL, 16); colors[color_count].r = (color_val >> 16) & 0xFF; colors[color_count].g = (color_val >> 8) & 0xFF; colors[color_count].b = color_val & 0xFF; color_count++; } } } // last color if (color_count < 4) { color_part = colors_str.substring(last_comma + 1); if (color_part.length() > 0) { long color_val = strtol(color_part.substring(1).c_str(), NULL, 16); colors[color_count].r = (color_val >> 16) & 0xFF; colors[color_count].g = (color_val >> 8) & 0xFF; colors[color_count].b = color_val & 0xFF; color_count++; } } if (color_count > 0) { int text_len = text_str.length(); int part_len = text_len / color_count; for (int i = 0; i < color_count; i++) { colors[i].start_index = i * part_len; } } short x = 0; short y = 0; if (server.hasArg("x") && server.arg("x") != "") { x = server.arg("x").toInt(); } if (server.hasArg("y") && server.arg("y") != "") { y = server.arg("y").toInt(); } addNewMultiColor(text, colors, color_count, false, x, y, slowness, true, is_small, disappear_time, is_repeating); server.send(200, "text/plain", "OK"); } else { server.send(400, "text/plain", "Invalid arguments for /multicolor-text"); } } void handleModifyText() { if (server.hasArg("id") && server.hasArg("color")) { unsigned char id = server.arg("id").toInt(); String colorStr = server.arg("color"); uint32_t color = strtol(colorStr.substring(1).c_str(), NULL, 16); String text_str = server.arg("text"); char text[TEXT_MAX_LENGTH + 1]; text_str.toCharArray(text, TEXT_MAX_LENGTH + 1); unsigned char slowness = server.arg("slowness").toInt(); modifyTextNodeById(id, color, text, slowness); server.send(200, "text/plain", "OK"); } else { server.send(400, "text/plain", "Invalid arguments for /modify-text"); } } void handleModifyMultiColorText() { if (server.hasArg("id") && server.hasArg("colors") && server.hasArg("text")) { unsigned char id = server.arg("id").toInt(); String colors_str = server.arg("colors"); String text_str = server.arg("text"); char text[TEXT_MAX_LENGTH + 1]; text_str.toCharArray(text, TEXT_MAX_LENGTH + 1); RGBWithIndex colors[4]; int color_count = 0; String color_part = ""; int last_comma = -1; for (int i = 0; i < colors_str.length() && color_count < 4; i++) { if (colors_str.charAt(i) == ',') { color_part = colors_str.substring(last_comma + 1, i); last_comma = i; if (color_part.length() > 0) { long color_val = strtol(color_part.substring(1).c_str(), NULL, 16); colors[color_count].r = (color_val >> 16) & 0xFF; colors[color_count].g = (color_val >> 8) & 0xFF; colors[color_count].b = color_val & 0xFF; color_count++; } } } if (color_count < 4) { color_part = colors_str.substring(last_comma + 1); if (color_part.length() > 0) { long color_val = strtol(color_part.substring(1).c_str(), NULL, 16); colors[color_count].r = (color_val >> 16) & 0xFF; colors[color_count].g = (color_val >> 8) & 0xFF; colors[color_count].b = color_val & 0xFF; color_count++; } } if (color_count > 0) { int text_len = text_str.length(); int part_len = text_len / color_count; for (int i = 0; i < color_count; i++) { colors[i].start_index = i * part_len; } } modifyMultiColorTextNodeById(id, text, colors, color_count); server.send(200, "text/plain", "OK"); } else { server.send(400, "text/plain", "Invalid arguments for /modify-multicolor-text"); } } void handleGetNodes() { StaticJsonDocument<2048> jsonDoc; JsonArray nodes = jsonDoc.to(); for (int i = 0; i < MAX_TEXT_NODES_COUNT; i++) { if (text_nodes[i].disappear_time != 0) { JsonObject node = nodes.createNestedObject(); node["id"] = text_nodes[i].id; node["text"] = text_nodes[i].content; node["color"] = text_nodes[i].color; node["x"] = text_nodes[i].pos_x; node["y"] = text_nodes[i].pos_y; node["slowness"] = text_nodes[i].scroll_slowness; node["is_repeating"] = text_nodes[i].is_repeating; node["type"] = "text"; node["slowness"] = text_nodes[i].scroll_slowness; } } for (int i = 0; i < MAX_TEXT_NODES_COUNT; i++) { if (multi_color_text_node[i].disappear_time != 0) { JsonObject node = nodes.createNestedObject(); node["id"] = multi_color_text_node[i].id; node["text"] = multi_color_text_node[i].content; JsonArray colors = node.createNestedArray("colors"); for(int j = 0; j < multi_color_text_node[i].color_count; j++) { JsonObject color = colors.createNestedObject(); color["r"] = multi_color_text_node[i].colors[j].r; color["g"] = multi_color_text_node[i].colors[j].g; color["b"] = multi_color_text_node[i].colors[j].b; } node["x"] = multi_color_text_node[i].pos_x; node["y"] = multi_color_text_node[i].pos_y; node["slowness"] = multi_color_text_node[i].scroll_slowness; node["is_repeating"] = multi_color_text_node[i].is_repeating; node["type"] = "multi-color"; node["slowness"] = multi_color_text_node[i].scroll_slowness; } } String jsonString; serializeJson(jsonDoc, jsonString); server.send(200, "application/json", jsonString); } void start_server() { WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi..."); } Serial.println("Connected to WiFi"); Serial.println(WiFi.localIP()); server.on("/", handleRoot); server.on("/nodes", HTTP_GET, handleGetNodes); server.on("/text", HTTP_POST, handleText); server.on("/modify-text", HTTP_POST, handleModifyText); server.on("/multicolor-text", HTTP_POST, handleMulticolorText); server.on("/modify-multicolor-text", HTTP_POST, handleModifyMultiColorText); server.on("/brightness", HTTP_POST, handleBrightness); server.on("/upload-page", HTTP_GET, handleUploadPage); server.on("/upload-bmp", HTTP_POST, []() { if (upload_error_message.length() > 0) { server.send(400, "text/plain", upload_error_message); } else { server.send(200, "text/plain", "Upload OK"); } }, handleBmpUpload); server.begin(); } void handle_server() { server.handleClient(); }