edit commit

This commit is contained in:
2025-02-24 20:23:44 +03:00
parent ed404edcbd
commit 5590a1c834
3 changed files with 202 additions and 149 deletions

View File

@ -3,8 +3,9 @@
* List of features * List of features
+ Edit text in real time via HomeAssistiant + Edit text in real time via HomeAssistiant
+ Split sentences into lines (features) + Split sentences into lines (features)
+ Enable/disable text scrolling (features) + Add a drawing as a binary image
+ Add a drawing as a binary image (features) + Panel operation modes(-> Text scroll -> Image ->) || (static image + static text)
+ OTA (Over The Air Updates) (features)
+ State of panel + State of panel
+ Remote panel on/off + Remote panel on/off
# Docs for API # Docs for API
@ -13,3 +14,4 @@
+ /api/text - endpoint for editing text (POST) + /api/text - endpoint for editing text (POST)
+ /api/led - endpint for on/off panel (POST) + /api/led - endpint for on/off panel (POST)
+ /api/led - endpoint for check on/off panel (GET), return "{"led":"true"}" if panel ON and "{"led":"false"}" if panel OFF + /api/led - endpoint for check on/off panel (GET), return "{"led":"true"}" if panel ON and "{"led":"false"}" if panel OFF
+ /api/state - endpoint for swith operation modes (POST), body: ({"State" : "1"} -> static image and static text) || body: ({"State" : "2"} -> -> Text scroll -> Image ->)

25
output.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef _OUTPUT_
#define _OUTPUT_
byte output[16 * 32] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
const int dataSize = sizeof(output) / sizeof(output[0]);
#endif

328
panel.ino
View File

@ -1,4 +1,4 @@
//----------------include libraries----------------// //------------------Include Libraries------------------//
#include <WiFi.h> #include <WiFi.h>
#include <WebServer.h> #include <WebServer.h>
#include <DMD32.h> #include <DMD32.h>
@ -6,193 +6,219 @@
#include <SPIFFS.h> #include <SPIFFS.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
//-------------------------------------------------// #include "data/output.h"
//------------------------------------------------------//
//----------------settings panel----------------// #define DEBUG 1
#define FONT Font_BOLD //Default font
#define DISPLAYS_ACROSS 1 //number of panels in width //------------------Settings for panel------------------//
#define DISPLAYS_DOWN 1 //number of panels in height #define FONT Font_BOLD
#define DISPLAYS_ACROSS 1
#define DISPLAYS_DOWN 1
DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN); DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN);
//----------------------------------------------// //-------------------------------------------------------//
//----------------settings local wifi----------------// //------------------Wi-Fi Settings----------------------//
const char *ssid = "SKBKIT"; //edit for your local wifi const char *ssid = "SKBKIT";
const char *password = "skbkit2024"; //edit for your local wifi const char *password = "skbkit2024";
//---------------------------------------------------// IPAddress staticIP(10, 131, 170, 4);
//----------------settings web server----------------//
AsyncWebServer server(80);
IPAddress staticIP(10, 131, 170, 4); //edit for your local ip
IPAddress gateway(10, 131, 170, 1); IPAddress gateway(10, 131, 170, 1);
IPAddress subnet(255, 255, 255, 0); IPAddress subnet(255, 255, 255, 0);
//---------------------------------------------------// //-------------------------------------------------------//
//----------------other variables----------------// //------------------Web Server Settings-----------------//
String displayText = "Привет из СКБ \"КИТ\""; //change the default label AsyncWebServer server(80);
//-------------------------------------------------------//
//------------------Other Variables---------------------//
String displayText = "Привет из СКБ \"КИТ\"";
bool isScrolling = true;
bool showImage = false;
unsigned long lastScrollTime = 0;
unsigned long scrollDuration = 5000;
unsigned long imageStartTime = 0;
hw_timer_t *timer = NULL; hw_timer_t *timer = NULL;
bool panel = true; //-------------------------------------------------------//
//-----------------------------------------------//
//---Search SPI panel---// //------------------Wi-Fi Connection Setup-------------//
void IRAM_ATTR triggerScan() { void setupWiFi() {
dmd.scanDisplayBySPI();
}
//----------------------//
void reader() {}
void setup() {
Serial.begin(115200);
dmd.selectFont(Font_BOLD);
WiFi.begin(ssid, password); WiFi.begin(ssid, password);
SPIFFS.begin(true);
pinMode(22, OUTPUT);
//---Configured WebServer---//
if (!WiFi.config(staticIP, gateway, subnet)) { if (!WiFi.config(staticIP, gateway, subnet)) {
Serial.println("Failed to configure Static IP"); Serial.println("Failed to configure Static IP");
} else { } else {
Serial.println("Static IP configured!"); Serial.println("Static IP configured!");
} }
//--------------------------//
//---Start file system---//
// File file = SPIFFS.open("/output.txt", "r");
// if(!file){Serial.println("file open failed");}
// Serial.println("Содержимое файла:");
// while(file.available()){
// Serial.write(file.read());
// }
// Serial.println();
// file.close();
// File root = SPIFFS.open("/");
// if(!root){
// Serial.println("Ошибка открытия директории");
// return;
// }
// if(!root.isDirectory()){
// Serial.println("Не является директорией!");
// return;
// }
// File file = root.openNextFile();
// while(file){
// Serial.println(file.name());
// file = root.openNextFile();
//}
//-----------------------//
//---Start the WiFi---//
while (WiFi.status() != WL_CONNECTED) { while (WiFi.status() != WL_CONNECTED) {
delay(500); delay(500);
Serial.print("."); Serial.print(".");
} }
Serial.println("\nConnected to WiFi"); Serial.println("\nConnected to WiFi");
Serial.println(WiFi.localIP()); Serial.println(WiFi.localIP());
//--------------------//
//---Request to check the status of the panel---//
server.on("/api/led", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(200, "application/json", panel ? "{\"state\": \"true\"}" : "{\"state\": \"false\"}");
});
//----------------------------------------------//
//---Request to change the status of the panel---//
/*server.on("/api/led", HTTP_POST,
[](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
{
AsyncWebServerRequest *req = request;
Serial.println(String("data=") + (char*)data);
// // Serial.print(request);
// if (req->hasParam("plain", true)) {
// String panelState = req->getParam("panel", true)->value();
// Serial.println(panelState);
// if (panelState == "on") {
// Serial.println("Panel on!");
// displayText = "";
// panel = true;
// } else if (panelState == "off") {
// panel = false;
// Serial.println("Panel off!");
// digitalWrite(22, LOW);
// dmd.clearScreen(1);
// displayText = "";
// Serial.println("Screen clear");
// }
// }
req->send(200, "text/html", "");
});*/
server.on(
"/api/led", HTTP_POST,
[](AsyncWebServerRequest *request) {
// Serial.println("1");
// Serial.println(request->url() );
// Serial.println(request->method());
},
[](AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) {
//Serial.println("2");
},
[](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
String dataRequest = String((char *)data);
Serial.println(dataRequest);
if (dataRequest == "{\"panel\" : \"on\"}") {
Serial.println("Panel on");
} else if (dataRequest == "{\"panel\" : \"off\"}") {
panel = false;
Serial.println("Panel off!");
digitalWrite(22, LOW);
dmd.clearScreen(1);
displayText = "";
Serial.println("Screen clear");
} }
//-------------------------------------------------------//
//------------------Web Server Handlers-----------------//
void handleText(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
JsonDocument jsonReq;
String dataRequest = String((char *)data, len);
DeserializationError error = deserializeJson(jsonReq, dataRequest);
if (error) {
Serial.println(F("deserializeJson() failed"));
request->send(400, "text/html", "");
return;
}
if (jsonReq["text"].is<String>()) {
displayText = jsonReq["text"].as<String>();
}
request->send(200, "text/html; charset=UTF-8", "");
}
void handleStateChange(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
JsonDocument jsonReq;
String dataRequest = String((char *)data, len);
DeserializationError error = deserializeJson(jsonReq, dataRequest);
if (error) {
request->send(400, "text/html", "");
return;
}
String state = jsonReq["state"];
if (state == "1") {
dmd.clearScreen(true); // Reset the screen
displayText = "Привет из СКБ \"КИТ\"";
} else if (state == "2") {
isScrolling = true;
lastScrollTime = millis();
showImage = false;
} else if (state == "3") {
showImage = true;
imageStartTime = millis();
}
request->send(200, "text/html", ""); request->send(200, "text/html", "");
});
//-----------------------------------------------//
//---A text modification request---//
server.on("/api/text", HTTP_POST, [](AsyncWebServerRequest *request) {
if (request->hasParam("text", true)) {
displayText = request->getParam("text", true)->value();
} }
request->send(200, "text/html; charset=UTF-8", //-------------------------------------------------------//
"<html><body><h2>Text set to:</h2><p>" + displayText + "</p></body></html>");
//------------------Panel Management--------------------//
void handlePanelState(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
JsonDocument jsonReq;
String dataRequest = String((char *)data, len);
DeserializationError error = deserializeJson(jsonReq, dataRequest);
if (error) {
request->send(400, "text/html", "");
return;
}
String state = jsonReq["panel"];
if (state == "on") {
displayText = "Привет из СКБ \"КИТ\"";
} else if (state == "off") {
displayText = "";
}
request->send(200, "text/html", "");
}
//-------------------------------------------------------//
//------------------Display Binary Array Function------//
void displayBinaryArray() {
for (int y = 0; y < 32; y++) {
for (int x = 0; x < 16; x++) {
int idx = x * 32 + y;
if (output[idx] == 1) {
dmd.writePixel(x, y, GRAPHICS_NORMAL, true);
} else {
dmd.writePixel(x, y, GRAPHICS_NORMAL, false);
}
}
}
}
//-------------------------------------------------------//
//------------------Marquee Scroll Function-------------//
void scrollText() {
dmd.drawMarquee(displayText.c_str(), displayText.length(), (32 * DISPLAYS_ACROSS) - 1, 0);
long start = millis();
long timer = start;
bool ret = false;
while (!ret) {
if ((timer + 30) < millis()) {
ret = dmd.stepMarquee(-1, 0);
timer = millis();
}
}
if (millis() - lastScrollTime >= scrollDuration) {
isScrolling = false;
showImage = true;
imageStartTime = millis();
}
}
//-------------------------------------------------------//
//------------------Timer Interrupt Function------------//
void IRAM_ATTR triggerScan() {
dmd.scanDisplayBySPI();
}
//-------------------------------------------------------//
//------------------Setup Function-----------------------//
void setup() {
Serial.begin(115200);
dmd.selectFont(FONT);
SPIFFS.begin(true);
pinMode(22, OUTPUT);
setupWiFi();
server.on("/api/led", HTTP_GET, [](AsyncWebServerRequest *request) {
String state = isScrolling ? "true" : "false";
request->send(200, "application/json", "{\"state\": \"" + state + "\"}");
}); });
//---------------------------------//
server.on("/api/led", HTTP_POST, [](AsyncWebServerRequest *request) {}, NULL, handlePanelState);
server.on("/api/text", HTTP_POST, [](AsyncWebServerRequest *request) {}, NULL, handleText);
server.on("/api/state", HTTP_POST, [](AsyncWebServerRequest *request) {}, NULL, handleStateChange);
server.begin(); server.begin();
//---Start the timer---//
uint8_t cpuClock = ESP.getCpuFreqMHz(); uint8_t cpuClock = ESP.getCpuFreqMHz();
timer = timerBegin(0, cpuClock, true); timer = timerBegin(0, cpuClock, true);
timerAttachInterrupt(timer, &triggerScan, true); timerAttachInterrupt(timer, &triggerScan, true);
timerAlarmWrite(timer, 300, true); timerAlarmWrite(timer, 300, true);
timerAlarmEnable(timer); timerAlarmEnable(timer);
//--------------------//
dmd.clearScreen(true); //Clearing the screen
}
void loop() {
//---Screen cleaning and text output---//
dmd.clearScreen(true); dmd.clearScreen(true);
dmd.drawMarquee(displayText.c_str(), displayText.length(), (32 * DISPLAYS_ACROSS) - 1, 0); }
//-------------------------------------// //-------------------------------------------------------//
long start = millis(); //------------------Loop Function------------------------//
long timer = start; void loop() {
boolean ret = false; dmd.clearScreen(true);
//---Scrolling text---// if (isScrolling) {
while (!ret) { scrollText();
if ((timer + 30) < millis()) { }
ret = dmd.stepMarquee(-1, 0); // Прокрутка текста
timer = millis(); if (showImage) {
displayBinaryArray();
if (millis() - imageStartTime >= 5000) {
showImage = false;
isScrolling = true;
lastScrollTime = millis();
} }
} }
//--------------------//
delay(500);
} }
//-------------------------------------------------------//