edit commit
This commit is contained in:
@ -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
25
output.h
Normal 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
|
320
panel.ino
320
panel.ino
@ -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---//
|
//------------------Web Server Handlers-----------------//
|
||||||
server.on("/api/led", HTTP_GET, [](AsyncWebServerRequest *request) {
|
void handleText(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
|
||||||
request->send(200, "application/json", panel ? "{\"state\": \"true\"}" : "{\"state\": \"false\"}");
|
JsonDocument jsonReq;
|
||||||
});
|
String dataRequest = String((char *)data, len);
|
||||||
//----------------------------------------------//
|
DeserializationError error = deserializeJson(jsonReq, dataRequest);
|
||||||
|
|
||||||
//---Request to change the status of the panel---//
|
if (error) {
|
||||||
/*server.on("/api/led", HTTP_POST,
|
Serial.println(F("deserializeJson() failed"));
|
||||||
[](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
|
request->send(400, "text/html", "");
|
||||||
{
|
return;
|
||||||
AsyncWebServerRequest *req = request;
|
}
|
||||||
Serial.println(String("data=") + (char*)data);
|
|
||||||
// // Serial.print(request);
|
|
||||||
|
|
||||||
// if (req->hasParam("plain", true)) {
|
if (jsonReq["text"].is<String>()) {
|
||||||
// String panelState = req->getParam("panel", true)->value();
|
displayText = jsonReq["text"].as<String>();
|
||||||
// Serial.println(panelState);
|
}
|
||||||
|
|
||||||
// if (panelState == "on") {
|
request->send(200, "text/html; charset=UTF-8", "");
|
||||||
// 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(
|
void handleStateChange(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
|
||||||
"/api/led", HTTP_POST,
|
JsonDocument jsonReq;
|
||||||
[](AsyncWebServerRequest *request) {
|
String dataRequest = String((char *)data, len);
|
||||||
// Serial.println("1");
|
DeserializationError error = deserializeJson(jsonReq, dataRequest);
|
||||||
// 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");
|
|
||||||
}
|
|
||||||
request->send(200, "text/html", "");
|
|
||||||
});
|
|
||||||
//-----------------------------------------------//
|
|
||||||
|
|
||||||
//---A text modification request---//
|
if (error) {
|
||||||
server.on("/api/text", HTTP_POST, [](AsyncWebServerRequest *request) {
|
request->send(400, "text/html", "");
|
||||||
if (request->hasParam("text", true)) {
|
return;
|
||||||
displayText = request->getParam("text", true)->value();
|
}
|
||||||
|
|
||||||
|
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", "");
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------//
|
||||||
|
|
||||||
|
//------------------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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
request->send(200, "text/html; charset=UTF-8",
|
}
|
||||||
"<html><body><h2>Text set to:</h2><p>" + displayText + "</p></body></html>");
|
}
|
||||||
|
//-------------------------------------------------------//
|
||||||
|
|
||||||
|
//------------------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);
|
||||||
}
|
}
|
||||||
|
//-------------------------------------------------------//
|
||||||
|
Reference in New Issue
Block a user