2023-01-22 21:47:03 +00:00
|
|
|
#include <Arduino.h>
|
|
|
|
|
2023-11-23 07:02:12 +00:00
|
|
|
#include <Homie.h>
|
2023-02-08 19:17:58 +00:00
|
|
|
|
2023-11-25 13:31:07 +00:00
|
|
|
HomieNode displayNode("display", "Display", "display");
|
2023-11-23 21:47:45 +00:00
|
|
|
HomieNode settingsNode("settings", "Settings", "settings");
|
2023-11-23 07:02:12 +00:00
|
|
|
HomieNode resultNode("result", "Result", "result");
|
2023-02-08 19:17:58 +00:00
|
|
|
|
2023-01-31 21:50:25 +00:00
|
|
|
#include "flipdot.h"
|
2023-02-08 19:17:58 +00:00
|
|
|
#include "image.h"
|
2023-01-22 21:47:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-02-08 19:17:58 +00:00
|
|
|
Image flip;
|
2023-01-31 20:28:49 +00:00
|
|
|
|
2023-01-22 21:47:03 +00:00
|
|
|
unsigned long loopmillis=0;
|
|
|
|
unsigned long last_update=0;
|
2023-01-24 22:46:52 +00:00
|
|
|
|
2023-11-23 21:47:45 +00:00
|
|
|
bool clearFirst=true;
|
|
|
|
bool optimizeClear=false;
|
|
|
|
bool optimizeSet=false;
|
|
|
|
|
|
|
|
uint8_t stringToBool(String s);
|
2023-02-08 19:17:58 +00:00
|
|
|
|
2023-11-23 07:02:12 +00:00
|
|
|
bool presetHandler(const HomieRange& range, const String& value);
|
2023-11-23 21:47:45 +00:00
|
|
|
bool orderHandler(const HomieRange& range, const String& value);
|
|
|
|
bool dataHandler(const HomieRange& range, const String& value);
|
2023-11-23 22:50:16 +00:00
|
|
|
bool controlHandler(const HomieRange& range, const String& value);
|
2023-11-25 13:31:07 +00:00
|
|
|
bool textHandler(const HomieRange& range, const String& value) ;
|
2024-01-09 21:15:54 +00:00
|
|
|
bool textLargeHandler(const HomieRange& range, const String& value) ;
|
2023-11-23 21:47:45 +00:00
|
|
|
|
|
|
|
bool clearFirstHandler(const HomieRange& range, const String& value);
|
|
|
|
bool optimizeClearHandler(const HomieRange& range, const String& value);
|
|
|
|
bool optimizeSetHandler(const HomieRange& range, const String& value);
|
2023-11-23 07:02:12 +00:00
|
|
|
|
|
|
|
/*bool globalInputHandler(const HomieNode& node, const HomieRange& range, const String& property, const String& value) {
|
|
|
|
Homie.getLogger() << "Global input handler. Received on node " << node.getId() << ": " << property << " = " << value << endl;
|
|
|
|
return false;
|
|
|
|
}*/
|
|
|
|
|
2023-11-23 21:47:45 +00:00
|
|
|
String error; //used to buffer error messages to be send via mqtt
|
2023-01-22 21:47:03 +00:00
|
|
|
|
|
|
|
void setup() {
|
2023-02-08 19:17:58 +00:00
|
|
|
flip.init();
|
2023-12-03 20:58:09 +00:00
|
|
|
flip.setDisplayMirror(true,true); //true,true=mirror both axis (rotate 180deg)
|
2023-01-22 21:47:03 +00:00
|
|
|
|
|
|
|
Serial.begin(115200);
|
2023-09-20 17:22:59 +00:00
|
|
|
|
2023-11-23 07:02:12 +00:00
|
|
|
//Setup Homie
|
2024-01-09 21:15:54 +00:00
|
|
|
Homie_setFirmware("flipdot", "0.1.2");
|
2023-11-25 13:31:07 +00:00
|
|
|
displayNode.advertise("preset").settable(presetHandler);
|
|
|
|
displayNode.advertise("data").settable(dataHandler);
|
|
|
|
displayNode.advertise("order").settable(orderHandler);
|
|
|
|
displayNode.advertise("control").settable(controlHandler);
|
|
|
|
displayNode.advertise("text").settable(textHandler);
|
2024-01-09 21:15:54 +00:00
|
|
|
displayNode.advertise("textlarge").settable(textLargeHandler);
|
2023-11-23 21:47:45 +00:00
|
|
|
settingsNode.advertise("clearfirst").settable(clearFirstHandler);
|
|
|
|
settingsNode.advertise("optimizeclear").settable(optimizeClearHandler);
|
|
|
|
settingsNode.advertise("optimizeset").settable(optimizeSetHandler);
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-11-23 07:02:12 +00:00
|
|
|
//Homie.setGlobalInputHandler(globalInputHandler);
|
|
|
|
Homie.setup();
|
|
|
|
|
2023-11-23 21:47:45 +00:00
|
|
|
error="";
|
|
|
|
|
2023-01-22 21:47:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void loop() {
|
2023-11-23 07:02:12 +00:00
|
|
|
Homie.loop();
|
2023-01-22 21:47:03 +00:00
|
|
|
loopmillis = millis();
|
2023-01-31 20:28:49 +00:00
|
|
|
|
2023-02-08 20:00:15 +00:00
|
|
|
static unsigned long last_change=0;
|
2023-11-23 07:02:12 +00:00
|
|
|
//static bool color=0;
|
2023-09-20 17:22:59 +00:00
|
|
|
|
2023-11-23 07:02:12 +00:00
|
|
|
/*
|
2023-09-20 17:22:59 +00:00
|
|
|
static uint8_t image=0;
|
|
|
|
if (loopmillis-last_change >= 1000*10)
|
2023-02-08 20:00:15 +00:00
|
|
|
{
|
2023-09-20 17:22:59 +00:00
|
|
|
|
2023-11-23 07:02:12 +00:00
|
|
|
//Serial.print("Change to Solid color ="); Serial.println(color);
|
|
|
|
|
|
|
|
//flip.setBuffer_solid(color);
|
|
|
|
//color=1-color;
|
|
|
|
|
2023-09-20 17:22:59 +00:00
|
|
|
|
|
|
|
|
2023-11-22 18:32:33 +00:00
|
|
|
|
2023-09-20 17:22:59 +00:00
|
|
|
switch (image){
|
|
|
|
case 0:
|
|
|
|
flip.setBuffer_Image1();
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
flip.setBuffer_Image2();
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
flip.setBuffer_Image3();
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
flip.setBuffer_Image4();
|
|
|
|
break;
|
|
|
|
|
2023-11-22 18:32:33 +00:00
|
|
|
case 4:
|
|
|
|
flip.setBuffer_Image5();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5:
|
|
|
|
flip.setBuffer_Image6();
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
flip.setBuffer_Image7();
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
flip.setBuffer_Image8();
|
|
|
|
break;
|
|
|
|
|
2023-09-20 17:22:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
image++;
|
2023-11-22 18:32:33 +00:00
|
|
|
//image+=1+random(8-2);
|
|
|
|
image%=8;
|
2023-09-20 17:22:59 +00:00
|
|
|
|
|
|
|
|
2023-02-08 20:29:10 +00:00
|
|
|
|
2023-11-23 07:02:12 +00:00
|
|
|
|
|
|
|
//uint8_t _randomvalue=random(128);
|
|
|
|
//Serial.print("set buffer random. ");
|
|
|
|
//Serial.println(_randomvalue);
|
|
|
|
//flip.setBuffer_random(_randomvalue);
|
|
|
|
|
|
|
|
|
|
|
|
//for (uint8_t _x=0;_x<COLUMNS;_x++) {
|
|
|
|
// uint8_t _y=(sin( loopmillis/100000.0 + _x/5.0 )/2+0.5)*15+0.5;
|
|
|
|
// uint16_t _row=pow(2, _y);
|
|
|
|
// flip.setBufferColumn(_x,_row);
|
|
|
|
//}
|
|
|
|
|
2023-11-22 18:32:33 +00:00
|
|
|
|
2023-02-13 18:58:45 +00:00
|
|
|
|
|
|
|
|
2023-02-08 20:00:15 +00:00
|
|
|
|
|
|
|
last_change=loopmillis;
|
|
|
|
}
|
2023-11-23 07:02:12 +00:00
|
|
|
*/
|
2023-02-08 20:00:15 +00:00
|
|
|
|
2023-11-23 21:47:45 +00:00
|
|
|
UpdateReturn result=flip.updateByColumn(clearFirst,optimizeClear,optimizeSet); //0=not finished, 1=finished
|
2023-11-22 18:32:33 +00:00
|
|
|
//UpdateReturn result=flip.updateByColumn(true,false,false); //0=not finished, 1=finished <- most simple
|
2023-11-23 21:47:45 +00:00
|
|
|
static UpdateReturn last_result=finished;
|
|
|
|
if (last_result==nochange && result!=last_result) { //was finished but has just started updating again
|
|
|
|
last_change=loopmillis; //display started changing dots. set starting time.
|
|
|
|
}
|
|
|
|
last_result=result;
|
2023-02-11 16:31:49 +00:00
|
|
|
if (result == finished) //just finished
|
2023-02-08 20:29:10 +00:00
|
|
|
{
|
|
|
|
unsigned long duration=millis()-last_change;
|
|
|
|
Serial.print("Last Change took "); Serial.print(duration); Serial.println(" ms");
|
|
|
|
Serial.print("Update max took "); Serial.print(flip.updateDuration); Serial.println(" ms");
|
2023-11-23 07:02:12 +00:00
|
|
|
resultNode.setProperty("duration").send((String)duration);
|
|
|
|
resultNode.setProperty("updateduration").send((String)flip.updateDuration);
|
2023-02-08 20:29:10 +00:00
|
|
|
flip.updateDuration=0; //reset
|
2023-11-23 22:50:16 +00:00
|
|
|
|
2023-02-11 17:51:33 +00:00
|
|
|
|
2023-02-08 20:29:10 +00:00
|
|
|
}
|
2023-11-23 21:47:45 +00:00
|
|
|
|
|
|
|
if (error.length()>0) { //is there an error to send?
|
|
|
|
resultNode.setProperty("error").send(error);
|
|
|
|
error=""; //clear
|
|
|
|
}
|
2023-01-22 21:47:03 +00:00
|
|
|
}
|
2023-11-23 07:02:12 +00:00
|
|
|
|
|
|
|
|
2023-11-23 21:47:45 +00:00
|
|
|
bool orderHandler(const HomieRange& range, const String& value) {
|
|
|
|
Serial.print("Order payload="); Serial.println(value);
|
|
|
|
if (value=="ascending") {
|
|
|
|
flip.resetOrder(true);
|
|
|
|
}else if (value=="descending") {
|
|
|
|
flip.resetOrder(false);
|
|
|
|
}else if (value=="shuffle") {
|
|
|
|
flip.shuffleOrder(8);
|
|
|
|
}else if (value=="random") {
|
|
|
|
flip.shuffleOrder(flip.getW());
|
|
|
|
}else{
|
|
|
|
return false;
|
|
|
|
}
|
2023-11-25 13:31:07 +00:00
|
|
|
displayNode.setProperty("order").send(value);
|
2023-11-23 21:47:45 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-11-23 07:02:12 +00:00
|
|
|
bool presetHandler(const HomieRange& range, const String& value) {
|
2023-11-23 21:47:45 +00:00
|
|
|
Serial.print("Preset payload="); Serial.println(value);
|
2023-11-23 07:02:12 +00:00
|
|
|
if (value == "white"){
|
|
|
|
flip.setBuffer_solid(1);
|
|
|
|
Homie.getLogger() << "Preset is White" << endl;
|
|
|
|
}else if(value == "black"){
|
|
|
|
flip.setBuffer_solid(0);
|
|
|
|
Homie.getLogger() << "Preset is Black" << endl;
|
2023-11-25 13:31:07 +00:00
|
|
|
}else if(value == "bumblebee"){
|
|
|
|
flip.setBuffer_Preset_Bumblebee();
|
|
|
|
Homie.getLogger() << "Preset is bumblebee" << endl;
|
|
|
|
}else if(value == "datamatrixctdo"){
|
|
|
|
flip.setBuffer_Preset_Datamatrixctdo();
|
|
|
|
Homie.getLogger() << "Preset is datamatrixctdo" << endl;
|
2023-11-23 21:47:45 +00:00
|
|
|
}else if(value == "random"){
|
|
|
|
flip.setBuffer_random(50);
|
2023-11-25 13:31:07 +00:00
|
|
|
}else if(value == "uptime") {
|
2023-11-25 11:23:15 +00:00
|
|
|
flip.setBuffer_solid(0);
|
2023-11-25 13:31:07 +00:00
|
|
|
flip.addBuffer_text((String)(millis()/1000),0,0);
|
2023-11-23 07:02:12 +00:00
|
|
|
}else{
|
2023-11-23 21:47:45 +00:00
|
|
|
error="preset \""+value+"\" not found";
|
2023-11-23 07:02:12 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-11-25 13:31:07 +00:00
|
|
|
displayNode.setProperty("preset").send(value);
|
2023-11-23 07:02:12 +00:00
|
|
|
|
|
|
|
return true;
|
2023-11-23 21:47:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool dataHandler(const HomieRange& range, const String& value) {
|
|
|
|
Serial.print("Data Received: "); Serial.println(value);
|
|
|
|
Serial.print("Length: "); Serial.println(value.length());
|
|
|
|
/*payload can be either be Binary/String:
|
|
|
|
"10101101011010.."
|
|
|
|
"1 111 11 11 1 1..."
|
|
|
|
"a_aa_aa_aa_aa__a_a_a.."
|
|
|
|
|
|
|
|
Or Int (unsigned 16 bit. comma separated for each column)
|
|
|
|
"837491,12347,592,920,114,0,12,45125,..."
|
|
|
|
*/
|
|
|
|
if (value.length()==flip.getW()*flip.getH()) { //if characters matches pixels then its likely binary
|
|
|
|
Serial.println("Using byString");
|
|
|
|
flip.setBuffer_byString(value,error);
|
|
|
|
}else{
|
|
|
|
Serial.println("Using byInt");
|
|
|
|
flip.setBuffer_byInt(value,error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool clearFirstHandler(const HomieRange& range, const String& value) {
|
|
|
|
Serial.print("clearFirstHandler payload="); Serial.println(value);
|
|
|
|
uint8_t res=stringToBool(value);
|
|
|
|
if (res==0){
|
|
|
|
clearFirst=false;
|
|
|
|
}else if(res==1){
|
|
|
|
clearFirst=true;
|
|
|
|
}else{
|
|
|
|
error="no valid input";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
settingsNode.setProperty("clearfirst").send((String)res);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool optimizeClearHandler(const HomieRange& range, const String& value) {
|
|
|
|
Serial.print("OptimizeClearHandler payload="); Serial.println(value);
|
|
|
|
uint8_t res=stringToBool(value);
|
|
|
|
if (res==0){
|
|
|
|
optimizeClear=false;
|
|
|
|
}else if(res==1){
|
|
|
|
optimizeClear=true;
|
|
|
|
}else{
|
|
|
|
error="no valid input";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
settingsNode.setProperty("optimizeclear").send((String)res);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool optimizeSetHandler(const HomieRange& range, const String& value) {
|
|
|
|
Serial.print("OptimizeSetHandler payload="); Serial.println(value);
|
|
|
|
uint8_t res=stringToBool(value);
|
|
|
|
if (res==0){
|
|
|
|
optimizeSet=false;
|
|
|
|
}else if(res==1){
|
|
|
|
optimizeSet=true;
|
|
|
|
}else{
|
|
|
|
error="no valid input";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
settingsNode.setProperty("optimizeset").send((String)res);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t stringToBool(String s) {
|
|
|
|
String param=s;
|
|
|
|
param.toLowerCase();
|
|
|
|
if (param == "true" || param == "1" || param == "on" || param == "yes" || param == "y"){
|
|
|
|
return 1;
|
|
|
|
}else if(param == "false" || param == "0" || param == "off" || param== "no" || param == "n"){
|
|
|
|
return 0;
|
|
|
|
}else{
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
}
|
2023-11-23 22:50:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool controlHandler(const HomieRange& range, const String& value) {
|
|
|
|
Serial.print("Control payload="); Serial.println(value);
|
|
|
|
if (value == "redraw"){
|
|
|
|
flip.redraw();
|
|
|
|
}else if(value == "black"){
|
|
|
|
flip.setBuffer_solid(0);
|
|
|
|
flip.redraw();
|
|
|
|
}else{
|
|
|
|
error="preset \""+value+"\" not found";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-11-25 13:31:07 +00:00
|
|
|
displayNode.setProperty("control").send(value);
|
2023-11-23 22:50:16 +00:00
|
|
|
|
|
|
|
return true;
|
2023-11-25 13:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool textHandler(const HomieRange& range, const String& value) {
|
|
|
|
Serial.print("Text payload="); Serial.println(value);
|
|
|
|
|
2023-11-25 13:42:59 +00:00
|
|
|
String row1;
|
|
|
|
String row2;
|
|
|
|
|
2023-11-25 13:31:07 +00:00
|
|
|
uint8_t linebreakindex=value.indexOf("\n"); //search for "\n" in string. 255 if not found
|
|
|
|
Serial.print("Linebreakindex="); Serial.println(linebreakindex);
|
2023-11-25 13:42:59 +00:00
|
|
|
if (linebreakindex!=255) { //found linebreak
|
|
|
|
row1=value.substring(0,linebreakindex);
|
|
|
|
row2=value.substring(linebreakindex+1);
|
|
|
|
}else{
|
2023-11-25 13:31:07 +00:00
|
|
|
|
2023-11-25 13:42:59 +00:00
|
|
|
row1=value.substring(0,flip.getW()/6);
|
|
|
|
row2=value.substring(flip.getW()/6);
|
|
|
|
row2.trim(); //remove leading and trailing whitespaces
|
|
|
|
String overflow=row2.substring(flip.getW()/6); //save text that did not fit
|
|
|
|
row2=row2.substring(0,flip.getW()/6);
|
2023-11-25 13:31:07 +00:00
|
|
|
|
2023-11-25 13:42:59 +00:00
|
|
|
if (overflow.length()>0) {
|
|
|
|
error="Text overflow:"+overflow;
|
|
|
|
Serial.println("Text overflow");
|
|
|
|
}
|
2023-11-25 13:31:07 +00:00
|
|
|
}
|
2023-11-25 13:42:59 +00:00
|
|
|
|
2023-11-25 13:31:07 +00:00
|
|
|
Serial.print("Row1="); Serial.println(row1);
|
|
|
|
Serial.print("Row2="); Serial.println(row2);
|
|
|
|
|
|
|
|
|
|
|
|
flip.setBuffer_solid(0); //clear
|
|
|
|
|
|
|
|
if (row2.length()<=0){
|
|
|
|
Serial.println("Only one row");
|
|
|
|
flip.addBuffer_text(row1,0,4);
|
|
|
|
}else{
|
|
|
|
Serial.println("Two rows");
|
|
|
|
flip.addBuffer_text(row1,0,0);
|
|
|
|
flip.addBuffer_text(row2,0,8);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-01-09 21:15:54 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool textLargeHandler(const HomieRange& range, const String& value) {
|
|
|
|
Serial.print("Text Large payload="); Serial.println(value);
|
|
|
|
|
|
|
|
String row1=value;
|
|
|
|
|
|
|
|
Serial.print("Row="); Serial.println(row1);
|
|
|
|
|
|
|
|
flip.setBuffer_solid(0); //clear
|
|
|
|
|
|
|
|
flip.addBuffer_text(row1,255,0,1); //large font, centered
|
|
|
|
|
2023-11-25 13:31:07 +00:00
|
|
|
return true;
|
2023-11-23 22:50:16 +00:00
|
|
|
}
|