380 lines
13 KiB
Arduino
380 lines
13 KiB
Arduino
#include <WiFi.h>
|
|
#include <WebServer.h>
|
|
#include <ArduinoJson.h>
|
|
#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("global_id") && server.hasArg("color"))
|
|
{
|
|
unsigned char global_id = server.arg("global_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();
|
|
modifyTextNodeByGlobalId(global_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("global_id") && server.hasArg("colors") && server.hasArg("text"))
|
|
{
|
|
unsigned char global_id = server.arg("global_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;
|
|
}
|
|
}
|
|
|
|
modifyMultiColorTextNodeByGlobalId(global_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<JsonArray>();
|
|
|
|
for (int i = 0; i < MAX_TEXT_NODES_COUNT; i++)
|
|
{
|
|
if (text_nodes[i].disappear_time != 0)
|
|
{
|
|
JsonObject node = nodes.createNestedObject();
|
|
node["global_id"] = text_nodes[i].global_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["global_id"] = multi_color_text_node[i].global_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) {
|
|
pixels.clear();
|
|
scrollAllScrollableTexts(true);
|
|
pixels.show();
|
|
delay(50);
|
|
}
|
|
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();
|
|
}
|