diff --git a/config.h b/config.h index d97c080..a076dec 100644 --- a/config.h +++ b/config.h @@ -8,6 +8,7 @@ #define DISPLAY_MAX_Y PANEL_PIXEL_COUNT - 1 #define TEXT_MAX_LENGTH 64 +#define MAX_MULTI_COLOR_TEXT_NODES_COUNT $ #define MAX_TEXT_NODES_COUNT 4 #define MAX_IMAGES_SAVED 2 diff --git a/index.h b/index.h index 6a0aff4..f65f655 100644 --- a/index.h +++ b/index.h @@ -21,11 +21,16 @@ const char index_html[] PROGMEM = R"rawliteral( height: 200px; margin-top: 20px; } + .text-controls { + margin-top: 20px; + } + .text-input-group { + margin-bottom: 10px; + }

LED Panel Control

-
@@ -34,25 +39,20 @@ const char index_html[] PROGMEM = R"rawliteral( 100

- +
+

Send Text

+
+ + + +
+
+ + +
+

- - diff --git a/ledy.ino b/ledy.ino index c655c89..fd58139 100644 --- a/ledy.ino +++ b/ledy.ino @@ -16,8 +16,10 @@ Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); TextNode text_nodes[MAX_TEXT_NODES_COUNT]; +MultiColorTextNode multi_color_text_node[MAX_TEXT_NODES_COUNT]; Cursor cursor; -short ever_created_text_nodes = 0; +unsigned char ever_created_text_nodes = 0; +unsigned char ever_created_multi_color_text_nodes = 0; Image saved_images[MAX_IMAGES_SAVED] = { {},{} @@ -133,6 +135,121 @@ void scrollAllScrollableTexts(bool split_scroll_mode = false) } } +void addNewMultiColor +( + char text[TEXT_MAX_LENGTH + 1], RGBWithIndex colors[4], unsigned char color_count, bool handle_pos_via_cursor = true, short pos_x = 0, short pos_y = 0, + unsigned char scroll_slowness = 1, bool is_scrolling = true, bool is_small = true +) +{ + unsigned char text_length = strlen(text); + if (text_length == 0){return;} + for (unsigned char i = 0; i < MAX_TEXT_NODES_COUNT; i++) + { + if (multi_color_text_node[i].is_deleted) + { + strncpy(multi_color_text_node[i].content, text, TEXT_MAX_LENGTH); + multi_color_text_node[i].color_count = color_count; + for(int j = 0; j < color_count; j++) + { + multi_color_text_node[i].colors[j] = colors[j]; + } + + if (handle_pos_via_cursor) + { + multi_color_text_node[i].pos_x = cursor.x; + multi_color_text_node[i].pos_y = cursor.y; + } + else + { + multi_color_text_node[i].pos_x = pos_x; + multi_color_text_node[i].pos_y = pos_y; + } + + multi_color_text_node[i].characterSize.height = is_small ? SMALL_TEXT_HEIGHT : MEDIUM_TEXT_HEIGHT; + multi_color_text_node[i].characterSize.width = is_small ? SMALL_TEXT_WIDTH : MEDIUM_TEXT_WIDTH; + multi_color_text_node[i].character_count = text_length; + multi_color_text_node[i].scroll_slowness = scroll_slowness; + multi_color_text_node[i].is_scrolling = is_scrolling; + multi_color_text_node[i].is_deleted = false; + ever_created_multi_color_text_nodes++; + + if (handle_pos_via_cursor) + { + cursor.x += (multi_color_text_node[i].characterSize.width + 1) * text_length; + } + break; + } + } +} + +short getMultiColorTextNodeX2(MultiColorTextNode *node) +{ + if (node->character_count == 0) return node->pos_x; + return node->pos_x + (node->characterSize.width * node->character_count) + (node->character_count - 1) - 1; +} + +void scrollAllMultiColorTexts(bool split_scroll_mode = false) +{ + for (unsigned char i = 0; i < MAX_TEXT_NODES_COUNT; i++) + { + if (multi_color_text_node[i].is_deleted) {continue;} + + if (multi_color_text_node[i].is_scrolling) + { + if (multi_color_text_node[i].scroll_slowness > multi_color_text_node[i].scroll_progress) + { + multi_color_text_node[i].scroll_progress++; + } + else + { + multi_color_text_node[i].scroll_progress = 0; + + short x1 = multi_color_text_node[i].pos_x; + short x2 = getMultiColorTextNodeX2(&multi_color_text_node[i]); + if (split_scroll_mode || multi_color_text_node[i].pos_y < 7) + { + if (x2 < 0) + { + multi_color_text_node[i].is_deleted = true; + continue; + } + multi_color_text_node[i].pos_x--; + } + else + { + if (x1 > DISPLAY_MAX_X) + { + multi_color_text_node[i].is_deleted = true; + continue; + } + multi_color_text_node[i].pos_x++; + } + } + } + + cursor.x = multi_color_text_node[i].pos_x; + cursor.y = multi_color_text_node[i].pos_y; + + uint32_t current_color = pixels.Color(multi_color_text_node[i].colors[0].r, multi_color_text_node[i].colors[0].g, multi_color_text_node[i].colors[0].b); + unsigned char color_index = 0; + + for (unsigned char j = 0; j < multi_color_text_node[i].character_count; j++) + { + if (color_index < multi_color_text_node[i].color_count - 1 && multi_color_text_node[i].colors[color_index+1].start_index == j) + { + color_index++; + current_color = pixels.Color(multi_color_text_node[i].colors[color_index].r, multi_color_text_node[i].colors[color_index].g, multi_color_text_node[i].colors[color_index].b); + } + char ch = multi_color_text_node[i].content[j]; + if (ch < '!' || ch > '~') + { + ch = ' '; + } + drawCharacter(font7x5[ch - ' '], multi_color_text_node[i].characterSize.height, multi_color_text_node[i].characterSize.width, current_color, &cursor); + } + } +} + void drawImageFromMemoryByIndex(unsigned char image_index, short pos_x, short pos_y, unsigned char brightness) { Image* img = &saved_images[image_index]; @@ -143,12 +260,12 @@ void drawImageFromMemoryByIndex(unsigned char image_index, short pos_x, short po }; for (unsigned char y = 0; y < img->height; y++) + { + for (unsigned char x = 0; x < img->width; x++) { - for (unsigned char x = 0; x < img->width; x++) - { - setPixel(x + pos_x, y + pos_y, pixels.Color(dimBy(img->pixels[y][x].r), dimBy(img->pixels[y][x].g), dimBy(img->pixels[y][x].b))); - } + setPixel(x + pos_x, y + pos_y, pixels.Color(dimBy(img->pixels[y][x].r), dimBy(img->pixels[y][x].g), dimBy(img->pixels[y][x].b))); } + } } void setup() @@ -162,6 +279,9 @@ void setup() addNewTextNode("NET", 0xFF050505, false, 0, 0); addNewTextNode("AWAIT", 0xFF050505, false, 0, 9); + RGBWithIndex colors[2] = {RGBWithIndex(255, 0, 0, 0), RGBWithIndex(0, 0, 255, 6)}; + addNewMultiColor("HELLO WORLD", colors, 2, false, 0, 0, 1, true, true); + pixels.show(); start_server(); } @@ -170,19 +290,8 @@ void loop() { pixels.clear(); handle_server(); - - if (saved_images[0].width > 0) - { - drawImageFromMemoryByIndex(0, 0, 0, brightness); - } - else - { - if (text_nodes[0].is_deleted && text_nodes[2].is_deleted) - { - - } - scrollAllScrollableTexts(); - } + scrollAllScrollableTexts(); + scrollAllMultiColorTexts(); pixels.show(); } \ No newline at end of file diff --git a/lowLevel.h b/lowLevel.h index dd2a8db..811033e 100644 --- a/lowLevel.h +++ b/lowLevel.h @@ -6,14 +6,11 @@ #include "structs.h" extern Adafruit_NeoPixel pixels; -extern unsigned char saved_images_count; extern Cursor cursor1; -extern uint32_t saved_imaged[2][16][16]; // Function declarations void setPixel(short x, short y, uint32_t color); uint32_t getPixelColor(short x, short y); -void drawImageFromSaved(short offset_x, short offset_y, unsigned char i); void drawCharacterPart(const bool* character_row, unsigned char width, uint32_t color, short start_x, short start_y); void drawCharacter(const bool (*character)[5], unsigned char height, unsigned char width, uint32_t color, Cursor (*used_cursor)); void fillPixels(short x1, short y1, short x2, short y2, uint32_t color); diff --git a/lowLevel.ino b/lowLevel.ino index ebf2798..a3099bf 100644 --- a/lowLevel.ino +++ b/lowLevel.ino @@ -1,60 +1,6 @@ #include "lowLevel.h" -unsigned char saved_images_count = 0; -uint32_t saved_imaged[2][16][16] = -{ - { - { - 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFF00, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000 - }, - { - 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000 - } - } -}; void setPixel(short x, short y, uint32_t color) { @@ -76,28 +22,7 @@ uint32_t getPixelColor(short x, short y) return pixels.getPixelColor(y + (x * PANEL_PIXEL_COUNT)); } -void drawImageFromSaved(short offset_x, short offset_y, unsigned char i) -{ - for (int row = 0; row < 16; row++) - { - for (int col = 0; col < 16; col++) - { - uint32_t px_color = saved_imaged[i][row][col]; - if (!px_color) - { - continue; - } - short pixel_x = col + offset_x; - short pixel_y = row + offset_y; - if (pixel_x >= 0 && pixel_x < NUMPIXELS && - pixel_y >= 0 && pixel_y < NUMPIXELS) - { - setPixel(pixel_x, pixel_y, px_color); - } - } - } -} void drawCharacterPart(const bool* character_row, unsigned char height, uint32_t color, short start_x, short start_y) { diff --git a/maker.html b/maker.html index 42e4a1a..66d57cf 100644 --- a/maker.html +++ b/maker.html @@ -230,6 +230,41 @@ .copied-message.show { opacity: 1; } + + .text-controls { + margin: 20px 0; + padding: 20px; + background-color: #161b22; + border-radius: 8px; + border: 1px solid #30363d; + text-align: center; + } + + .text-controls h3 { + margin-top: 0; + color: #c9d1d9; + } + + .text-input-group { + display: flex; + justify-content: center; + gap: 15px; + margin: 15px 0; + } + + .text-input-group input[type="text"] { + width: 300px; + padding: 8px; + font-size: 16px; + border: 1px solid #30363d; + border-radius: 6px; + background-color: #0d1117; + color: #c9d1d9; + } + + .text-buttons { + margin-top: 15px; + } @@ -265,6 +300,18 @@ +
+

Send Text

+
+ + +
+
+ + +
+
+

Export Format

@@ -513,6 +560,35 @@ }); } + function sendText(position) { + const text = document.getElementById('textInput').value; + const color = document.getElementById('textColorPicker').value; + + if (!text) { + alert('Please enter some text.'); + return; + } + + const formData = new FormData(); + formData.append('text', text); + formData.append('color', color); + formData.append('position', position); + + fetch('/text', { + method: 'POST', + body: new URLSearchParams(formData) + }) + .then(response => { + if (!response.ok) { + alert('Error sending text.'); + } + }) + .catch(error => { + console.error('Error:', error); + alert('Error sending text.'); + }); + } + // Clear all cells function clearGrid() { for (let i = 0; i < currentRows; i++) { diff --git a/prototypes.h b/prototypes.h index 934a87a..78d1427 100644 --- a/prototypes.h +++ b/prototypes.h @@ -6,8 +6,8 @@ extern Adafruit_NeoPixel pixels; extern Image saved_images[MAX_IMAGES_SAVED]; -extern unsigned char saved_images_count; extern TextNode text_nodes[MAX_TEXT_NODES_COUNT]; +extern MultiColorTextNode multi_color_text_node[MAX_TEXT_NODES_COUNT]; extern Cursor cursor; extern unsigned char brightness; @@ -18,6 +18,8 @@ void start_server(); void handle_server(); void addNewTextNode(char text[TEXT_MAX_LENGTH + 1], uint32_t color, bool handle_pos_via_cursor, short pos_x, short pos_y, unsigned char scroll_slowness, bool is_scrolling, bool is_small); void scrollAllScrollableTexts(bool split_scroll_mode); +void addNewMultiColor(char text[TEXT_MAX_LENGTH + 1], RGBWithIndex colors[4], unsigned char color_count, bool handle_pos_via_cursor, short pos_x, short pos_y, unsigned char scroll_slowness, bool is_scrolling, bool is_small); +void scrollAllMultiColorTexts(bool split_scroll_mode); void drawCharacter(const bool (*character)[5], unsigned char height, unsigned char width, uint32_t color, Cursor (*used_cursor)); diff --git a/server.ino b/server.ino index d8ce7a5..f7e87f5 100644 --- a/server.ino +++ b/server.ino @@ -96,14 +96,33 @@ void handleBmpUpload() { -void handleShowSaved() -{ - pixels.clear(); - drawImageFromMemoryByIndex(0, 0, 0, brightness); - pixels.show(); - server.send(200, "text/plain", "OK"); + +void handleText() { + if (server.hasArg("text") && server.hasArg("color") && server.hasArg("position") && server.hasArg("slowness")) { + String text_str = server.arg("text"); + String colorStr = server.arg("color"); + String position = server.arg("position"); + unsigned char slowness = server.arg("slowness").toInt(); + + 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 (position == "top") { + addNewTextNode(text, color, false, 43, 0, slowness, true, false); + } else if (position == "bottom") { + addNewTextNode(text, color, false, -text_str.length() * 6, 9, slowness, true, false); + } + + server.send(200, "text/plain", "OK"); + } else { + server.send(400, "text/plain", "Invalid arguments"); + } } + + void start_server() { WiFi.begin(ssid, password); @@ -116,8 +135,8 @@ void start_server() Serial.println(WiFi.localIP()); server.on("/", handleRoot); + server.on("/text", HTTP_POST, handleText); server.on("/brightness", HTTP_POST, handleBrightness); - server.on("/show-saved", handleShowSaved); server.on("/upload-page", HTTP_GET, handleUploadPage); server.on("/upload-bmp", HTTP_POST, []() { if (upload_error_message.length() > 0) { diff --git a/structs.h b/structs.h index 47dadbf..a9cbdbe 100644 --- a/structs.h +++ b/structs.h @@ -22,13 +22,13 @@ struct TextNode { char content[TEXT_MAX_LENGTH]; uint32_t color; - short pos_x; - short pos_y; struct CharacterSize { unsigned short height; unsigned short width; } characterSize; + short pos_x; + short pos_y; unsigned char character_count; unsigned char scroll_slowness; unsigned char scroll_progress; @@ -36,7 +36,7 @@ struct TextNode bool is_scrolling; - TextNode() : color(0), pos_x(0), pos_y(0), character_count(0), scroll_slowness(0), scroll_progress(0), characterSize({7,5}), is_deleted(true), is_scrolling() {} + TextNode() : color(0), pos_x(0), pos_y(0), character_count(0), scroll_slowness(0), scroll_progress(0), characterSize({7,5}), is_deleted(true), is_scrolling(true) {} }; struct RGB @@ -44,6 +44,41 @@ struct RGB unsigned char r; unsigned char g; unsigned char b; + + RGB() : r(0), g(0), b(0) {} +}; + +struct RGBWithIndex +{ + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char start_index; + + RGBWithIndex() : r(0), g(0), b(0), start_index(0) {} + RGBWithIndex(unsigned char r, unsigned char g, unsigned char b, unsigned char start_index) : r(r), g(g), b(b), start_index(start_index) {} +}; + +struct MultiColorTextNode +{ + char content[TEXT_MAX_LENGTH]; + RGBWithIndex colors[4]; + + struct CharacterSize + { + unsigned short height; + unsigned short width; + } characterSize; + short pos_x; + short pos_y; + unsigned char color_count; + unsigned char character_count; + unsigned char scroll_slowness; + unsigned char scroll_progress; + bool is_deleted; + bool is_scrolling; + + MultiColorTextNode() : pos_x(0), pos_y(0), character_count(0), scroll_slowness(0), scroll_progress(0), characterSize({7,5}), is_deleted(true), is_scrolling(true), color_count(0) {} }; struct Image