ESP8266 websockets woes!

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:

  1. Connect to PB

  2. 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}

  1. Read 3 different values located in both frames and store them

  2. Send a couple more commands, then close connection

  3. 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;
      



  
}

The ESP-Link project is very interesting, and there are some very smart people working on it. Maybe you can get some guidance in their chat (gitter chat):

In esp-link this is completely replaced by javascript in the browser to create interaction and AJAX requests to the esp8266 to get JSON data and submit changes

Source: https://jeelabs.org/book/1526d/index.html

If you have a question, please use the gitter chat link at the top of this page.

For quick support and questions chat at Chat at https://gitter.im/jeelabs/esp-link

Source: https://github.com/jeelabs/esp-link

use this instead

  server.send_P(200, "text/html", MAIN_page, sizeof(MAIN_page) );

you save lots of RAM

Thanks for the tip:)

When I press the submit button on the webpage, the command I'd put in the text field comes back in Serial Monitor as gibberish, do you have any idea why?

This is the part of the code that reads it and passes it to Serial Monitor:

void handleForm() {
 String PBCommand = server.arg("pbcommand"); 
 
client.onMessage([&](WebsocketsMessage message) 
  {
    client.send(PBCommand);
    Serial.print("PB Command:");
    Serial.println(PBCommand);
  });

This is the html part for the text field:

<p>PB Command:<br /><input name="pbcommand" type="text" value="{getConfig:true}" />

I changed the type value to string but got the same results:

PB Command:⸮T ⸮?⸮⸮?4f!@⸮⸮ ⸮?⸮⸮⸮⸮⸮?j!

It is also being repeated forever, there's something wrong in the LOOP part of the code but I can't figure it out.

If I were you I'do take babysteps till main goal is achieved, first off make sure you get the value sent from web page correctly before using it

the script above isn't looking good, how about this you remove the client thing block and up with something like this

void handleForm() {
 String PBCommand = server.arg("pbcommand"); 
 
 Serial.print("PB Command:");
 Serial.println(PBCommand);

 String s = "<a href='/'> Go Back </a>";
 server.send(200, "text/html", s); //Send web page
}

You're right, I rushed to put everything in the sketch and should take it easier and do one step at a time.

Removing the client part did the trick, it was responsible for the gibberish in the Serial Monitor. It was this line in particular:

client.onMessage([&](WebsocketsMessage message)

I kept the client.send(PBCommand); and now it is sent once, and I receive the proper response from the server.

I'm still struggling with hiding the "heartbeat" messages so the serial monitor doesn't keep on printing them, only the messages that come as responses from commands I sent. I don't understand if this has to be done on the websocket side or the serial.print side. Any pointers?

the way you are handling websocket callbacks is weird

can you take a look at HERE ?

that's a worked example, will get you guided on how/where/when you can correctly invoke the functions , some inside the main loop ie: poll()

others inside the setup() ie: onMessage() and onEvent()

Thanks for the pointer, I had checked the GitHub page before but didn't pay close attention apparently.

Everything is working correctly now, I can open/close the connection and send/receive ws either from the web page or with a button press.

Got another question if you don't mind. The code responsible for receiving, parsing the ws frame and closing the connection is in setup(), that's the only place it works correctly, if I place client.close anywhere else the connection simply stays open. Once I open the connection I want it to stay open for a few secs so I get all the frames needed, but the way it is now I receive the first and then it closes. Is there a non-blocking way of waiting within setup() I can try? I tried the

if((unsigned long)(millis() - time_now) > period){
        time_now = millis();

but it doesn't change anything, I guess it only works within loop().

Here's the bit within setup():

 // run callback when messages are received
  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.close();     
  }); 

Here's the whole sketch:

#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ArduinoJson.h>
#include <ArduinoWebsockets.h>

using namespace websockets;

#include <Wire.h>
#include <Adafruit_INA219.h>

Adafruit_INA219 ina219;

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 = 3;
int auxButtonState = 0;    // temporary variable for reading the button pin status

int period = 4000;
unsigned long time_now = 0;

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 = "COSMOTE-782506";
const char* password = "6sb4r2s46p88psre";
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 = 3;    // 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");
  } 
  
}



//===============================================================
// This routine is executed when you open its IP in browser
//===============================================================
void handleRoot() {
 String s = MAIN_page; //Read HTML contents
 server.send_P(200, "text/html", MAIN_page, sizeof(MAIN_page) );
}
//===============================================================
// This routine is executed when you press submit
//===============================================================
void handleForm() {
 String PBCommand = server.arg("pbcommand"); 


// try to connect to Websockets server
  bool connected = client.connect(websockets_server_host, websockets_server_port, "/");
  
  if (connected) 
  {

    client.send(PBCommand);
    Serial.print("PB Command:");
    Serial.println(PBCommand);
        
  } 
  else 
  {
    Serial.println("Not Connected!");
  }
 

 String s = "<a href='/'> Go Back </a>";
 server.send(200, "text/html", s); //Send web page


}



//==============================================================
//                  SETUP
//==============================================================
void setup(void){
  Serial.begin(115200,SERIAL_8N1,SERIAL_TX_ONLY);
  while (!Serial) {
      // will pause Zero, Leonardo, etc until serial console opens
      delay(1);
  }

  Wire.begin(0, 2);

uint32_t currentFrequency;   //INA219

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");


 // run callback when messages are received
  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.close();     
  });         
 
  // run callback when events are occuring
          client.onEvent(onEventsCallback);

if (! ina219.begin()) {
    Serial.println("Failed to find INA219 chip");
    while (1) { delay(10); }
  }
Serial.println("Measuring voltage and current with INA219 ...");


  
}
//==============================================================
//                     LOOP
//==============================================================
void loop(void){
  server.handleClient();          //Handle client requests


  // let the websockets client check for incoming messages
  if (client.available()) 
  { 
    client.poll();
  }


  float shuntvoltage = 0;
  float busvoltage = 0;
  float current_mA = 0;
  float loadvoltage = 0;
  float power_mW = 0;

  shuntvoltage = ina219.getShuntVoltage_mV();
  busvoltage = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  power_mW = ina219.getPower_mW();
  loadvoltage = busvoltage + (shuntvoltage / 1000);



// 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");


// try to connect to Websockets server
  bool connected = client.connect(websockets_server_host, websockets_server_port, "/");
  
  if (connected) 
  {

    client.send("{getConfig:true}");
    Serial.print("PB Command: {getConfig:true}");     
  } 
  else 
  {
    Serial.println("Not Connected!");
  }       
       }
     }

   }

   // save the reading.  Next time through the loop,
   // it'll be the lastButtonState:
   lastButtonState = reading;
      


if((unsigned long)(millis() - time_now) > period){
        time_now = millis();
        Serial.print("Bus Voltage:   "); Serial.print(busvoltage); Serial.println(" V");
  Serial.print("Shunt Voltage: "); Serial.print(shuntvoltage); Serial.println(" mV");
  Serial.print("Load Voltage:  "); Serial.print(loadvoltage); Serial.println(" V");
  Serial.print("Current:       "); Serial.print(current_mA); Serial.println(" mA");
  Serial.print("Power:         "); Serial.print(power_mW); Serial.println(" mW");
  Serial.println("");
        
    }

  
}

Thanks for your patience!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.