|
|
|
|
@ -3,54 +3,79 @@
|
|
|
|
|
#include <WiFiClientSecure.h>
|
|
|
|
|
#include <ArduinoOTA.h>
|
|
|
|
|
#include <time.h>
|
|
|
|
|
#include <PubSubClient.h>
|
|
|
|
|
#include <ArduinoJson.h>
|
|
|
|
|
|
|
|
|
|
#include "Telegram.h"
|
|
|
|
|
#include "FlipDotDrv.h"
|
|
|
|
|
#include "secrets.h"
|
|
|
|
|
|
|
|
|
|
String Text = "";
|
|
|
|
|
|
|
|
|
|
#define HOSTNAME "flipdottelegram"
|
|
|
|
|
|
|
|
|
|
/* ------------ MQTT variables -------------- */
|
|
|
|
|
WiFiClient espClient;
|
|
|
|
|
PubSubClient mqtt(espClient);
|
|
|
|
|
#define FLIPDOT_AVAILABILITY_TOPIC "flipdotdisplay/available"
|
|
|
|
|
#define FLIPDOT_SWITCH_TOPIC "flipdotdisplay/switch"
|
|
|
|
|
#define FLIPDOT_TEXT_TOPIC "flipdotdisplay/text"
|
|
|
|
|
void mqttRefresh();
|
|
|
|
|
void mqttCallback(char *topic, unsigned char *message, unsigned int length);
|
|
|
|
|
|
|
|
|
|
void publishOpen()
|
|
|
|
|
{
|
|
|
|
|
mqtt.publish(FLIPDOT_SWITCH_TOPIC "/state", "open");
|
|
|
|
|
|
|
|
|
|
JsonDocument doc;
|
|
|
|
|
doc["value"] = Text;
|
|
|
|
|
char output[256];
|
|
|
|
|
serializeJson(doc, output);
|
|
|
|
|
mqtt.publish(FLIPDOT_TEXT_TOPIC "/state", output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void publishClose()
|
|
|
|
|
{
|
|
|
|
|
mqtt.publish(FLIPDOT_SWITCH_TOPIC "/state", "close");
|
|
|
|
|
|
|
|
|
|
JsonDocument doc;
|
|
|
|
|
doc["value"] = Text;
|
|
|
|
|
char output[256];
|
|
|
|
|
serializeJson(doc, output);
|
|
|
|
|
mqtt.publish(FLIPDOT_TEXT_TOPIC "/state", output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ------------ MQTT variables -------------- */
|
|
|
|
|
bool OTAStarted;
|
|
|
|
|
void onOTAStart() {
|
|
|
|
|
void onOTAStart()
|
|
|
|
|
{
|
|
|
|
|
OTAStarted = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BearSSL::WiFiClientSecure secured_client;
|
|
|
|
|
WiFiClientSecure secured_client;
|
|
|
|
|
bool Start = false;
|
|
|
|
|
|
|
|
|
|
/* ------------ FlipDot variables -------------- */
|
|
|
|
|
#define WIDTH 96
|
|
|
|
|
#define HEIGHT 16
|
|
|
|
|
#define ROTATION 0 // 0 or 2 for 0 degrees or 180 degrees
|
|
|
|
|
#define MAX_CHAR_PER_LINE WIDTH/6
|
|
|
|
|
#define MAX_CHAR_PER_LINE WIDTH / 6
|
|
|
|
|
|
|
|
|
|
FlipDotDrv flip(WIDTH, HEIGHT, 0);
|
|
|
|
|
GFXcanvas1 canvas(WIDTH, HEIGHT);
|
|
|
|
|
|
|
|
|
|
void displayText(const char* text, bool background, int16_t x = -1, int16_t y = -1);
|
|
|
|
|
void displayText(const char *text, bool background, int16_t x = -1, int16_t y = -1);
|
|
|
|
|
|
|
|
|
|
String Text = "";
|
|
|
|
|
/* ------------ Telegram variables -------------- */
|
|
|
|
|
// BearSSL::WiFiClientSecure secured_client;
|
|
|
|
|
WiFiClientSecure secured_client;
|
|
|
|
|
|
|
|
|
|
void printOpen(String Args)
|
|
|
|
|
{
|
|
|
|
|
Text = "offen";
|
|
|
|
|
}
|
|
|
|
|
//TelegramCommand callbacks must fulfill a function pointer: void (*) (const String arguments)
|
|
|
|
|
void printOpen(String Args);
|
|
|
|
|
void printClose(String Args);
|
|
|
|
|
void printText(String Args);
|
|
|
|
|
|
|
|
|
|
void printClose(String Args)
|
|
|
|
|
{
|
|
|
|
|
Text = "leider zu";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void printText(String Args)
|
|
|
|
|
{
|
|
|
|
|
Serial.print("Display would show '");
|
|
|
|
|
Serial.print(Args);
|
|
|
|
|
Serial.println("'");
|
|
|
|
|
Text = Args;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TelegramCommand myCmdList[] {
|
|
|
|
|
{"/display_open", printOpen},
|
|
|
|
|
{"/display_close", printClose},
|
|
|
|
|
{"/display_text", printText},
|
|
|
|
|
TelegramCommand myCmdList[]{
|
|
|
|
|
{"/open", printOpen},
|
|
|
|
|
{"/close", printClose},
|
|
|
|
|
{"/text", printText},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
uint32_t myCmdListCnt = sizeof(myCmdList) / sizeof(myCmdList[0]);
|
|
|
|
|
@ -60,14 +85,14 @@ Telegram tele(secured_client, myCmdList, myCmdListCnt);
|
|
|
|
|
|
|
|
|
|
void setup()
|
|
|
|
|
{
|
|
|
|
|
uint8_t rawBuff[192] = {};
|
|
|
|
|
|
|
|
|
|
Serial.begin(115200);
|
|
|
|
|
Serial.println();
|
|
|
|
|
|
|
|
|
|
//rotate the Canvas so it is displayed in the correct orientation on the FlipDotDisplay
|
|
|
|
|
// rotate the Canvas so it is displayed in the correct orientation on the FlipDotDisplay
|
|
|
|
|
canvas.setRotation(ROTATION);
|
|
|
|
|
|
|
|
|
|
/* initialize the display with several checker template */
|
|
|
|
|
uint8_t rawBuff[192] = {};
|
|
|
|
|
Serial.println("Initializing Display");
|
|
|
|
|
flip.sendRaw(rawBuff, sizeof(rawBuff));
|
|
|
|
|
|
|
|
|
|
@ -105,10 +130,10 @@ void setup()
|
|
|
|
|
}
|
|
|
|
|
flip.sendRaw(rawBuff, sizeof(rawBuff));
|
|
|
|
|
|
|
|
|
|
// attempt to connect to Wifi network:
|
|
|
|
|
/* attempt to connect to Wifi network */
|
|
|
|
|
Serial.print("Connecting to Wifi SSID ");
|
|
|
|
|
Serial.print(WIFI_SSID);
|
|
|
|
|
WiFi.setHostname("flipdottelegram");
|
|
|
|
|
WiFi.setHostname(HOSTNAME);
|
|
|
|
|
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
|
|
|
|
while (WiFi.status() != WL_CONNECTED)
|
|
|
|
|
{
|
|
|
|
|
@ -120,6 +145,7 @@ void setup()
|
|
|
|
|
Serial.print("\nWiFi connected. IP address: ");
|
|
|
|
|
Serial.println(WiFi.localIP());
|
|
|
|
|
|
|
|
|
|
/* get internet time */
|
|
|
|
|
Serial.print("Retrieving time: ");
|
|
|
|
|
configTime(0, 0, "pool.ntp.org"); // get UTC time via NTP
|
|
|
|
|
configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "pool.ntp.org");
|
|
|
|
|
@ -132,13 +158,19 @@ void setup()
|
|
|
|
|
}
|
|
|
|
|
Serial.println(now);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ArduinoOTA.setHostname("flipdottelegram");
|
|
|
|
|
/* setup ArduinoOTA */
|
|
|
|
|
ArduinoOTA.setHostname(HOSTNAME);
|
|
|
|
|
ArduinoOTA.begin();
|
|
|
|
|
ArduinoOTA.onStart(onOTAStart);
|
|
|
|
|
|
|
|
|
|
/* initialize and connect telegram bot */
|
|
|
|
|
tele.init();
|
|
|
|
|
|
|
|
|
|
/* prepare MQTT client */
|
|
|
|
|
mqtt.setServer(MQTT_SERVER, 1883);
|
|
|
|
|
mqtt.setCallback(mqttCallback);
|
|
|
|
|
|
|
|
|
|
/* initialy print closed at the display */
|
|
|
|
|
printClose("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -148,29 +180,38 @@ void loop()
|
|
|
|
|
static unsigned long lastDisplayRefresh = now;
|
|
|
|
|
static unsigned long lastTelegramRefresh = now;
|
|
|
|
|
|
|
|
|
|
ArduinoOTA.handle();
|
|
|
|
|
/* OTA handling */
|
|
|
|
|
if (WiFi.status() == WL_CONNECTED)
|
|
|
|
|
{
|
|
|
|
|
ArduinoOTA.handle();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* MQTT handling */
|
|
|
|
|
mqttRefresh();
|
|
|
|
|
|
|
|
|
|
yield();
|
|
|
|
|
|
|
|
|
|
/* Telegram handling */
|
|
|
|
|
if ((now - lastTelegramRefresh) > 5000)
|
|
|
|
|
{
|
|
|
|
|
lastTelegramRefresh = now;
|
|
|
|
|
tele.cyclic(now);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FlipDot Handling */
|
|
|
|
|
if ((now - lastDisplayRefresh) > 2000)
|
|
|
|
|
{
|
|
|
|
|
lastDisplayRefresh = now;
|
|
|
|
|
//fill canvas with black pixels
|
|
|
|
|
// fill canvas with black pixels
|
|
|
|
|
canvas.fillScreen(0);
|
|
|
|
|
|
|
|
|
|
//if textlength is larger than possible on the display we need to scroll
|
|
|
|
|
// if textlength is larger than possible on the display we need to scroll
|
|
|
|
|
uint32_t TextLength = Text.length();
|
|
|
|
|
if (TextLength > (MAX_CHAR_PER_LINE - 6))
|
|
|
|
|
{
|
|
|
|
|
static uint32_t offset = 0;
|
|
|
|
|
|
|
|
|
|
//get the substring ffor this iteration
|
|
|
|
|
// get the substring ffor this iteration
|
|
|
|
|
String SubText = Text.substring(offset, offset + MAX_CHAR_PER_LINE);
|
|
|
|
|
|
|
|
|
|
if (SubText.length() < MAX_CHAR_PER_LINE)
|
|
|
|
|
@ -180,71 +221,228 @@ void loop()
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//increase the offset and reset it at the end of the scrol
|
|
|
|
|
// increase the offset and reset it at the end of the scrol
|
|
|
|
|
offset += 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//write substring to canvas
|
|
|
|
|
// write substring to canvas
|
|
|
|
|
displayText(SubText.c_str(), true, 0, 4);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//display current time on the left
|
|
|
|
|
// display current time on the left
|
|
|
|
|
tm timeinfo;
|
|
|
|
|
char buffer [6] {0};
|
|
|
|
|
if(getLocalTime(&timeinfo))
|
|
|
|
|
char buffer[6]{0};
|
|
|
|
|
if (getLocalTime(&timeinfo))
|
|
|
|
|
{
|
|
|
|
|
strftime(buffer, 6, "%R", &timeinfo);
|
|
|
|
|
}
|
|
|
|
|
displayText(buffer, true, 0, 4);
|
|
|
|
|
|
|
|
|
|
//display text on the right centered
|
|
|
|
|
// display text on the right centered
|
|
|
|
|
displayText(Text.c_str(), true, (6 + (MAX_CHAR_PER_LINE - 6 - TextLength) / 2) * 6, 4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//send canvas to display
|
|
|
|
|
// send canvas to display
|
|
|
|
|
flip.sendCanvas(&canvas);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void displayText(const char* text, bool background, int16_t x, int16_t y)
|
|
|
|
|
/* function to write text onto the canvas
|
|
|
|
|
*
|
|
|
|
|
* @param text - C string to be printed
|
|
|
|
|
* @param background - boolean switch if the background should be transparent (false) or black (true)
|
|
|
|
|
* @param x - position in X direction to write the Text, -1 centers the text
|
|
|
|
|
* @param y - position in Y direction to write the Text, -1 centers the text
|
|
|
|
|
*
|
|
|
|
|
* */
|
|
|
|
|
void displayText(const char *text, bool background, int16_t x, int16_t y)
|
|
|
|
|
{
|
|
|
|
|
// canvas.fillScreen(0);
|
|
|
|
|
canvas.setFont(NULL);
|
|
|
|
|
canvas.setTextSize(1);
|
|
|
|
|
canvas.setTextColor(1);
|
|
|
|
|
// canvas.fillScreen(0);
|
|
|
|
|
canvas.setFont(NULL);
|
|
|
|
|
canvas.setTextSize(1);
|
|
|
|
|
canvas.setTextColor(1);
|
|
|
|
|
|
|
|
|
|
int16_t cursor_x, cursor_y;
|
|
|
|
|
int16_t bounds_x, bounds_y;
|
|
|
|
|
uint16_t bounds_w, bounds_h;
|
|
|
|
|
canvas.getTextBounds(text, 0, 0, &bounds_x, &bounds_y, &bounds_w, &bounds_h);
|
|
|
|
|
int16_t cursor_x, cursor_y;
|
|
|
|
|
int16_t bounds_x, bounds_y;
|
|
|
|
|
uint16_t bounds_w, bounds_h;
|
|
|
|
|
canvas.getTextBounds(text, 0, 0, &bounds_x, &bounds_y, &bounds_w, &bounds_h);
|
|
|
|
|
|
|
|
|
|
if (x == -1)
|
|
|
|
|
{
|
|
|
|
|
// Zentriert
|
|
|
|
|
cursor_x = (WIDTH - bounds_w) / 2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cursor_x = x;
|
|
|
|
|
}
|
|
|
|
|
if (y == -1)
|
|
|
|
|
{
|
|
|
|
|
// Zentriert
|
|
|
|
|
cursor_y = (HEIGHT - bounds_h) / 2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cursor_y = y;
|
|
|
|
|
}
|
|
|
|
|
if (x == -1)
|
|
|
|
|
{
|
|
|
|
|
// centered
|
|
|
|
|
cursor_x = (WIDTH - bounds_w) / 2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cursor_x = x;
|
|
|
|
|
}
|
|
|
|
|
if (y == -1)
|
|
|
|
|
{
|
|
|
|
|
// centered
|
|
|
|
|
cursor_y = (HEIGHT - bounds_h) / 2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cursor_y = y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (background)
|
|
|
|
|
{
|
|
|
|
|
canvas.fillRect(cursor_x - 1, cursor_y - 1, bounds_w + 1, bounds_h + 1, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
canvas.setCursor(cursor_x, cursor_y);
|
|
|
|
|
canvas.print(text);
|
|
|
|
|
/* draw a black rectangle in the background of the text if it should not be transparent */
|
|
|
|
|
if (background)
|
|
|
|
|
{
|
|
|
|
|
canvas.fillRect(cursor_x - 1, cursor_y - 1, bounds_w + 1, bounds_h + 1, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* print the text to the destination on canvas */
|
|
|
|
|
canvas.setCursor(cursor_x, cursor_y);
|
|
|
|
|
canvas.print(text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void printOpen(String Args)
|
|
|
|
|
{
|
|
|
|
|
Text = "offen";
|
|
|
|
|
publishOpen();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void printClose(String Args)
|
|
|
|
|
{
|
|
|
|
|
Text = "leider zu";
|
|
|
|
|
publishClose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void printText(String Args)
|
|
|
|
|
{
|
|
|
|
|
Serial.print("Display would show '");
|
|
|
|
|
Serial.print(Args);
|
|
|
|
|
Serial.println("'");
|
|
|
|
|
Text = Args;
|
|
|
|
|
publishClose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void mqttRefresh()
|
|
|
|
|
{
|
|
|
|
|
if (WiFi.status() == WL_CONNECTED)
|
|
|
|
|
{
|
|
|
|
|
if (!mqtt.loop())
|
|
|
|
|
{
|
|
|
|
|
Serial.println("MQTT not connected");
|
|
|
|
|
|
|
|
|
|
if (mqtt.connect(HOSTNAME, MQTT_USER, MQTT_PASSWORD, FLIPDOT_AVAILABILITY_TOPIC, 2, true, "offline"))
|
|
|
|
|
{
|
|
|
|
|
mqtt.subscribe(FLIPDOT_SWITCH_TOPIC);
|
|
|
|
|
mqtt.subscribe(FLIPDOT_TEXT_TOPIC);
|
|
|
|
|
mqtt.publish(FLIPDOT_AVAILABILITY_TOPIC, "available");
|
|
|
|
|
|
|
|
|
|
auto mac = WiFi.macAddress();
|
|
|
|
|
mac.replace(":","_");
|
|
|
|
|
|
|
|
|
|
JsonDocument doc;
|
|
|
|
|
JsonObject device = doc["dev"].to<JsonObject>();
|
|
|
|
|
device["ids"] = mac;
|
|
|
|
|
device["name"] = "FlipDotDisplay";
|
|
|
|
|
device["mf"] = "-mf-";
|
|
|
|
|
device["mdl"] = "-mdl-";
|
|
|
|
|
device["sw"] = "-sw-";
|
|
|
|
|
device["sn"] = "-sn-";
|
|
|
|
|
device["hw"] = "-hw-";
|
|
|
|
|
|
|
|
|
|
JsonObject origin = doc["o"].to<JsonObject>();
|
|
|
|
|
origin["name"] = "Hobbyhimmel";
|
|
|
|
|
origin["sw"] = "-sw-";
|
|
|
|
|
origin["url"] = "https://hobbyhimmel.de";
|
|
|
|
|
|
|
|
|
|
JsonObject components = doc["cmps"].to<JsonObject>();
|
|
|
|
|
JsonObject sw = doc["FlipDotDisplaySwitch"].to<JsonObject>();
|
|
|
|
|
sw["unique_id"] = mac + "FlipDotDisplaySwitch";
|
|
|
|
|
sw["p"] = "switch";
|
|
|
|
|
sw["command_topic"] = FLIPDOT_SWITCH_TOPIC;
|
|
|
|
|
sw["state_topic"] = FLIPDOT_SWITCH_TOPIC"/state";
|
|
|
|
|
sw["payload_on"] = "open";
|
|
|
|
|
sw["payload_off"] = "close";
|
|
|
|
|
sw["qos"] = 0;
|
|
|
|
|
sw["retain"] = false;
|
|
|
|
|
|
|
|
|
|
JsonObject txt = doc["FlipDotDisplayText"].to<JsonObject>();
|
|
|
|
|
sw["unique_id"] = mac + "FlipDotDisplayText";
|
|
|
|
|
sw["p"] = "text";
|
|
|
|
|
sw["command_topic"] = FLIPDOT_TEXT_TOPIC;
|
|
|
|
|
sw["state_topic"] = FLIPDOT_TEXT_TOPIC"/state";
|
|
|
|
|
sw["qos"] = 0;
|
|
|
|
|
sw["retain"] = false;
|
|
|
|
|
|
|
|
|
|
doc["availability_topic "] = FLIPDOT_AVAILABILITY_TOPIC;
|
|
|
|
|
|
|
|
|
|
String jsonString;
|
|
|
|
|
serializeJson(doc, jsonString);
|
|
|
|
|
|
|
|
|
|
String discoveryTopic = "homeassistant/device/";
|
|
|
|
|
discoveryTopic += mac + "/config";
|
|
|
|
|
|
|
|
|
|
mqtt.publish(discoveryTopic.c_str(), jsonString.c_str());
|
|
|
|
|
Serial.print("Homeassistant Discovery was published: ");
|
|
|
|
|
Serial.println(jsonString);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
mqtt.disconnect();
|
|
|
|
|
Serial.println("* disconnected MQTT");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void mqttCallback(char *topic, unsigned char *message, unsigned int length)
|
|
|
|
|
{
|
|
|
|
|
String MessageString;
|
|
|
|
|
|
|
|
|
|
/* Convert all char* to Strings for simpler handling */
|
|
|
|
|
for (int i = 0; i < length; i++)
|
|
|
|
|
{
|
|
|
|
|
MessageString += message[i];
|
|
|
|
|
}
|
|
|
|
|
String ToppicString(topic);
|
|
|
|
|
|
|
|
|
|
/* if topic is the switch topic check for on, off, open, close */
|
|
|
|
|
if (ToppicString == FLIPDOT_SWITCH_TOPIC)
|
|
|
|
|
{
|
|
|
|
|
Serial.println("Switch topic received: " + MessageString);
|
|
|
|
|
|
|
|
|
|
if ((MessageString == "open") ||
|
|
|
|
|
(MessageString == "on"))
|
|
|
|
|
{
|
|
|
|
|
printOpen(MessageString);
|
|
|
|
|
}
|
|
|
|
|
else if ((MessageString == "close") ||
|
|
|
|
|
(MessageString == "off"))
|
|
|
|
|
{
|
|
|
|
|
printClose(MessageString);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if topic is the text topic try deserializing the json text*/
|
|
|
|
|
if (ToppicString == FLIPDOT_TEXT_TOPIC)
|
|
|
|
|
{
|
|
|
|
|
Serial.println("Text topic received: " + MessageString);
|
|
|
|
|
JsonDocument doc;
|
|
|
|
|
if (deserializeJson(doc, (const char*)message, length) == DeserializationError::Ok)
|
|
|
|
|
{
|
|
|
|
|
MessageString = doc["value"] | String("none"); //the pipe operand tells the default value, if "value" is not existing
|
|
|
|
|
|
|
|
|
|
if ((MessageString != "none"))
|
|
|
|
|
{
|
|
|
|
|
printText(MessageString);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Serial.print(FLIPDOT_TEXT_TOPIC);
|
|
|
|
|
Serial.println(" has no [\"value\"]!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Serial.print(FLIPDOT_TEXT_TOPIC);
|
|
|
|
|
Serial.println(" is no json!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|