Hello all,
I have a fantastic Pixelblaze LED controller that I want to send commands to using WebSocket frames.
Pixelblaze itself is based on a ESP8266, sets up a server and transmits something like a heartbeat every second with the following JSON string:
{"fps":436.5634,"vmerr":0,"vmerrpc":-1,"mem":10233,"exp":1,"renderType":1,"uptime":47696,"storageUsed":767558,"storageSize":1378241,"rr0":1,"rr1":14,"rebootCounter":0}
I'm trying to configure an ESP01S to do the following when a button connected to GPIO2 is pressed:
-
Connect to PB
-
Send a JSON frame to get certain configuration values.
This is the command: {getConfig:true}
Which returns two separate frames as follows:
{"name":"Pixelblaze_C058A4","brandName":"","pixelCount":60,"brightness":1,"maxBrightness":100,"colorOrder":"GRBW","dataSpeed":2000000,"ledType":5,"sequenceTimer":15,"sequencerMode":0,"runSequencer":false,"simpleUiMode":false,"discoveryEnable":true,"timezone":"","autoOffEnable":false,"autoOffStart":"00:00","autoOffEnd":"00:00","exp":1,"ver":"3.20"}
{"activeProgram":{"name":"02. Static 4 Segments","activeProgramId":"GBtBx5PvfSLjKuGcd","controls":{"hsvPickerColour1":[0.9433497536945813,0.8516802763819097,1],"hsvPickerColour2":[0.518867924528302,1,0.8313725490196079],"hsvPickerColour3":[0.01140350877192986,0.7786885245901639,0.9568627450980393],"hsvPickerColour4":[0.14965986394557818,0.7686274509803921,1],"sliderDepth1":0.3356,"sliderDepth2":0.5011,"sliderDepth3":0.2592}},"sequencerMode":0,"runSequencer":false}
-
Read 3 different values located in both frames and store them
-
Send a couple more commands, then close connection
-
Eventually stop using Serial monitor for debugging and have everything displayed in a webpage setup by ESP01s, including the ability to send WebSocket frames to PB from it.
I'm running into multiple issues.
No matter how much I've searched, I cannot find a way to open/close the connection when the button is pressed, it has to happen in SETUP, so I'm guessing the connection stays open constantly the way it is now.
In order to have the incoming frames displayed, I have to use the client.poll()
command. As a consequence, Serial Monitor keeps printing PB's "heartbeat" and I get flushed with frames I don't need and only make it harder to debug. Is there a way to exclude received packets? I tried searching for "JSON parsing filter" or similar, but couldn't find anything. In pseudo-code it would be something like "discard packets starting with {"fps":"
I have no idea how to correctly structure the client.onMessage
part in the LOOP section. The way it is now, if I press the button it loops the Response bit, even though everything else within the button loop is only executed once.
I also don't know how to parse and save the JSON values.
Sorry if it's too many questions, also please bear in mind that I have practically zero coding knowledge and it took me dozens of hours to get this far, so please be gentle:)
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ArduinoJson.h>
#include <WebSockets2_Generic.h> . // CLIENT
using namespace websockets2_generic; //CLIENT
WebsocketsClient client; //CLIENT
IPAddress local_IP(192, 168, 1, 95); //SERVER
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
int button = 0;
int auxButtonState = 0; // temporary variable for reading the button pin status
const char MAIN_page[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<body>
<h2>PB Websocket Test Page</h2>
<form action="/action_page">
<p>PB Command:<br /><input name="pbcommand" type="text" value="{getConfig:true}" />
<br /><br />
<input type="submit" value="Submit" /></p>
</form>
</body>
</html>
)=====";
//SSID and Password of your WiFi router
const char* ssid = "*********";
const char* password = "********";
const char* hostname = "CurrentSensor";
const char* websockets_server_host = "192.168.1.12"; //Enter server address CLIENT
const uint16_t websockets_server_port = 81; // Enter server port
ESP8266WebServer server(80); //Server on port 80
const int buttonPin = 0; // the number of the pushbutton pin
int buttonState; // the current reading from the input pin
int lastButtonState = LOW; // the previous reading from the input pin
long lastDebounceTime = 0; // the last time the output pin was toggled
long debounceDelay = 50; // the debounce time; increase if the output flickers
void onEventsCallback(WebsocketsEvent event, String data) //CLIENT
{
(void) data;
if (event == WebsocketsEvent::ConnectionOpened)
{
Serial.println("Connnection Opened");
}
else if (event == WebsocketsEvent::ConnectionClosed)
{
Serial.println("Connnection Closed");
}
else if (event == WebsocketsEvent::GotPing)
{
Serial.println("Got a Ping!");
}
else if (event == WebsocketsEvent::GotPong)
{
Serial.println("Got a Pong!");
}
}
//===============================================================
// This routine is executed when you open its IP in browser
//===============================================================
void handleRoot() {
String s = MAIN_page; //Read HTML contents
server.send(200, "text/html", s); //Send web page
}
//===============================================================
// This routine is executed when you press submit
//===============================================================
void handleForm() {
String PBCommand = server.arg("pbcommand");
client.onMessage([&](WebsocketsMessage message)
{
client.send(PBCommand);
client.poll();
Serial.print("PB Command:");
Serial.println(PBCommand);
});
String s = "<a href='/'> Go Back </a>";
server.send(200, "text/html", s); //Send web page
}
//==============================================================
// SETUP
//==============================================================
void setup(void){
Serial.begin(115200);
pinMode(button, INPUT);
WiFi.mode(WIFI_STA);
WiFi.config(local_IP, gateway, subnet);
WiFi.hostname(hostname);
WiFi.begin(ssid, password); //Connect to your WiFi router
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
//If connection successful show IP address in serial monitor
Serial.println("");
Serial.print("Connected to ");
Serial.println("WiFi");
Serial.print("IP address: ");
Serial.println(WiFi.localIP()); //IP address assigned to your ESP
server.on("/", handleRoot); //Which routine to handle at root location
server.on("/action_page", handleForm); //form action is handled here
server.begin(); //Start server
Serial.println("HTTP server started");
Serial.print("Connected to Wifi, Connecting to WebSockets Server @"); //CLIENT
Serial.println(websockets_server_host);
// run callback when messages are received
client.onMessage([&](WebsocketsMessage message)
{
//Serial.print("Got Message: ");
//Serial.println(message.data());
});
// run callback when events are occuring
client.onEvent(onEventsCallback);
// try to connect to Websockets server
bool connected = client.connect(websockets_server_host, websockets_server_port, "/");
if (connected)
{
Serial.println("Connected!");
String WS_msg = String("Hello Server!");
client.send(WS_msg);
}
else
{
Serial.println("Not Connected!");
}
}
//==============================================================
// LOOP
//==============================================================
void loop(void){
server.handleClient(); //Handle client requests
// let the websockets client check for incoming messages
if (client.available())
{
client.poll();
}
// read the state of the switch into a local variable:
int reading = digitalRead(buttonPin);
// If the switch changed, due to noise or pressing:
if (reading != lastButtonState) {
// reset the debouncing timer
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current state:
// if the button state has changed:
if (reading != buttonState) {
buttonState = reading;
// only toggle the LED if the new button state is HIGH
if (buttonState == LOW) {
Serial.print("Button Pressed");
//client.poll();
delay(500);
client.send("{getConfig:true}");
//client.poll();
client.onMessage([&](WebsocketsMessage message)
{
String payload = message.data(); //Get the response payload from server
Serial.println(payload); //Print request response payload
const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60;
DynamicJsonBuffer jsonBuffer(capacity);
// Parse JSON object
JsonObject& root = jsonBuffer.parseObject(payload);
if (!root.success()) {
Serial.println(F("Parsing failed!"));
return;
}
// Decode JSON/Extract values
Serial.println(F("Response:"));
Serial.println(root["pixelCount"].as<char*>());
Serial.println(root["brightness"].as<char*>());
});
client.send("{brightness:1}");
client.send("{getConfig:true}");
delay(2000);
client.send("{brightness:0.4}");
client.send("{getConfig:true}");
}
}
}
// save the reading. Next time through the loop,
// it'll be the lastButtonState:
lastButtonState = reading;
}