Arduino Socket.io shows disconnected when delay() is used - c

I am new to Arduino and facing an issue. I am implementing socket in Arduino for ESP8266 . It works as expected when I am not using delay() or not using someFunction(). As soon as i use delay or do some processing than i am getting connection disconnected one server socket.
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ArduinoJson.h>
#include <WebSocketsClient.h>
#include <SocketIOclient.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti;
SocketIOclient socketIO;
#define USE_SERIAL Serial1
void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
switch(type) {
case sIOtype_DISCONNECT:
USE_SERIAL.printf("[IOc] Disconnected!\n");
break;
case sIOtype_CONNECT:
USE_SERIAL.printf("[IOc] Connected to url: %s\n", payload);
break;
case sIOtype_EVENT:
USE_SERIAL.printf("[IOc] get event: %s\n", payload);
break;
case sIOtype_ACK:
USE_SERIAL.printf("[IOc] get ack: %u\n", length);
hexdump(payload, length);
break;
case sIOtype_ERROR:
USE_SERIAL.printf("[IOc] get error: %u\n", length);
hexdump(payload, length);
break;
case sIOtype_BINARY_EVENT:
USE_SERIAL.printf("[IOc] get binary: %u\n", length);
hexdump(payload, length);
break;
case sIOtype_BINARY_ACK:
USE_SERIAL.printf("[IOc] get binary ack: %u\n", length);
hexdump(payload, length);
break;
}
}
void setup() {
// USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
//Serial.setDebugOutput(true);
USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
// disable AP
if(WiFi.getMode() & WIFI_AP) {
WiFi.softAPdisconnect(true);
}
WiFiMulti.addAP("SSID", "passpasspass");
//WiFi.disconnect();
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
String ip = WiFi.localIP().toString();
USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());
// server address, port and URL
socketIO.begin("10.11.100.100", 8880);
// event handler
socketIO.onEvent(socketIOEvent);
}
unsigned long messageTimestamp = 0;
void loop() {
socketIO.loop();
uint64_t now = millis();
if(now - messageTimestamp > 2000) {
messageTimestamp = now;
// creat JSON message for Socket.IO (event)
DynamicJsonDocument doc(1024);
JsonArray array = doc.to<JsonArray>();
// add evnet name
// Hint: socket.on('event_name', ....
array.add("event_name");
// add payload (parameters) for the event
JsonObject param1 = array.createNestedObject();
param1["now"] = now;
// JSON to String (serializion)
String output;
serializeJson(doc, output);
// Send event
socketIO.sendEVENT(output);
// Print JSON for debugging
USE_SERIAL.println(output);
}
//NEED TO DO SOME PROCESSIONG
delay(2000);
someFuntion();
}
void someFunction(){
//some processing that consume the time
}

You are getting timeout on server side.
When you are making delay(10000) esp8266 is just hanging on some loop (as a delay implementation) - any other code is not executed due this time. So when the server is not getting any data it disconnects client.
The solution (easy way) is to make a new connection every time you want to send data. In pseudocode it will look like this:
void loop(){
connectToServer();
sendData();
Disconnect();
delay(1000);
SomeFunction();
}
Then it should work.
The more complicated method is to familiarize yourself with the concept of Events and callbacks and get rid of delay() function. Or try to use esp_rtos_sdk witch is asynchronous tasks concepts RTOS framework.
Have fun!
Edited:
After refreshed the code I'm able to tell more about it, so I'm editing the previous post.
The quickest way to make it work(partly) is to remove the delay(2000) statement - because this If() statement is handling sending messages in 2 seconds interval. As I see you may wan't to put this void someFunction() to this if() statement too. This will make "some processing" in 2 seconds interval.
socketIO.loop() is handling maintaining connection with server and sending event's to server so it has to be call as quick as plausible. In case with delay you are getting socket timeout and disconnection.
void loop() function must be shortest as plausible to maintain the connection. Probably it is 500ms or so - regarding to you experiments. So any delay's is not plausible. If you need more processing try to familiarize with event processing. The easiest way is to declare flags as Boolean and timers counting time intervals. When timer is lunched it's sets the flag as true and you may process function.
Websockets on ESP(or other embedded microprocessors) are hard for beginners because they are time dependent. You need to have short processing loop or some of asynchronous methods used.
I hope, that I help.

Related

Event-based task management using FreeRTOS

I'm trying to pick up C, using an esp32. While looking at exactly how FreeRTOS works, I found the following page regarding how to use the tasks, and best practices etc.
https://www.freertos.org/implementing-a-FreeRTOS-task.html
According to this page, to prevent starvation, tasks should be event based. Regarding what I am trying to achieve, I will try to provide a very simplified example.
Background
I have a LCD screen, which should display data from a sensor. The data shown on the LCD will be done using a task, which according to the documentation, should never exit and should be event driven to prevent starvation.
I have a way of controlling the data shown on the LCD screen, which would be a rotary encoder. This encoder can be clicked, which should refresh the sensor's data.
Question
How would I implement the event based tasks, which are described on the FreeRTOS page, in this specific context? I had a look at the documentation and the "simple" example projects on their github, but as a beginner within C and embedded, they were extremely overwhelming.
Simple demo code
void update_sensor_task(void *pvParameters)
{
// Ensure the task keeps on running
for( ; ; )
{
if(event_update_sensor) // How would I be able to notify the task that this should be run?
{
// update the data
}
}
// Tasks should not be returning, but if they happen to do so, ensure a clean exit
vTaskDelete(NULL);
}
void screen_temperature_task(void *pvParameters)
{
for(; ;)
{
if(event_sensor_updated)
{
// Update the lcd screen with the new data
}
}
vTaskDelete(NULL);
}
void on_rotary_clicked(void *pvParameters)
{
// Notify the sensor task that it should be updating?
}
EDIT:
By using what has been marked as the correct answer, I have managed to get it to work by implementing it the following way:
/* Queue used to send and receive the data */
QueueHandle_t xStructQueue = NULL;
/* Struct which shall be used to hold and pass around the data for the LCD screen*/
struct LcdData
{
int current_temp;
int current_humidity;
} xLcdData;
void initialize_queues(void)
{
xLcdData.current_humidity = 0;
xLcdData.current_temp = 0;
xStructQueue = xQueueCreate(
/* The maximum number of items the queue can hold*/
5,
/* The size of each struct, which the queue should be able to hold */
sizeof( xLcdData )
);
if(xStructQueue == NULL)
{
ESP_LOGE(TAG, "Queue has not been initialized successfully");
}
}
void screen_temperature_task_simplified(void *pvParameters)
{
int counter = 0;
for(; ;)
{
struct LcdData xReceivedStructure;
BaseType_t result;
result = xQueueReceive(xStructQueue, &xReceivedStructure, ( TickType_t ) 10);
if(result == pdPASS)
{
counter = counter + 1;
char snum_current_counter[12];
sprintf(snum_current_counter, "%d", counter);
i2c_lcd1602_clear (lcd_info);
i2c_lcd1602_write_string (lcd_info, snum_current_counter);
}
}
vTaskDelete(NULL);
}
void update_sensor_struct(void)
{
xLcdData.current_temp = DHT11_read().temperature;
xLcdData.current_humidity = DHT11_read().humidity;
// Log the results in the console
printf("Temperature is %d \n", xLcdData.current_temp);
printf("Humidity is %d\n", xLcdData.current_humidity);
ESP_LOGI(TAG, "Data has been updated");
}
void on_rotary_clicked_simplified()
{
ESP_LOGI(TAG, "Rotary encoder has been clicked!");
// Update the struct which holds the data
update_sensor_struct();
/* Send the entire struct to the queue */
xQueueSend(
/* The handle of the queue */
xStructQueue,
/* The adress of the struct which should be sent */
(void *) &xLcdData,
/* Block time of 0 says don't block if the queue is already full.
Check the value returned by xQueueSend() to know if the message
was sent to the queue successfully. */
( TickType_t ) 0
);
}
I use FRTOS and event driven development.
The typical flow here would be:
for(;;)
{
BaseType_t result;
result = xQueueReceive(LCD_Event_Queue, &someLCDEvent, QUEUE_TIMEOUT);
if (result == pdPASS)
{
/* We have new event data in someLCDEvent; Use that data to update the LCD */
}
else
{
/* No new event, do some brief idle-time processing if necessary */
}
}
In brief, wait up to QUEUE_TIMEOUT time for a new event to arrive.
If a new event arrives within that timeframe successfully, then process the data in that event and update your screen.
If a new event does not arrive, you have an opportunity to do some other maintenance work.
Designing and defining the structure-type of someLCDEvent, and putting data into the queue is a big topic, and will depend a lot on your specific project.

ESP8266 unwanted delay using client.readStringUntil(), with Microsoft SQL server

My goal is to read the number of button press within a period(30s), upload it to a Microsoft SQl table using a php script. On the table, there is another column which adds in accumulated sum of the presses, which will be echo-ed back by the php script, and be read by the ESP8266 board.
So everything went through: I can read the count, upload it, read the accumulated sum back. The issue is it always takes ~5s in order to get a value back from the server. Attached is my troubleshooting:Serial Windows
It takes almost instantly to connect to the server(~100ms) but 5s to read the data that be echo-ed.
My guess was the php script takes time to run to the line where it echo out the value, but same delay(~5s) happens even if I tried a simple script(just echo).
Is this how the server behaves (not giving out the data right away) or is this how Arduino code (client.readStringUntil()) behaves?
Here is my code:
#include <SPI.h>
#include <ESP8266WiFi.h>
// Wifi Connection config
const char* ssid = "------";
const char* password = "------";
WiFiServer server(80);
IPAddress dbserver(10,1,1,138);
void setup_wifi()
{
delay(10);
// We start by connecting to a WiFi network
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
}
void setup()
{
Serial.begin(115200);
// Connect to WiFi network
setup_wifi();
}
void loop ()
{
WiFiClient client;
const int httpPort = 80;
bool conn = client.connect(dbserver,httpPort);
if (!conn) {
Serial.println("connection failed");
return;
}
else {
// call php script on the server(echo 1 value)
client.print("GET /test10.php HTTP/1.1");
client.println("Host: 10.1.1.138");
client.println("Connection: close");
client.println(); // Empty line
// Read from the server
Serial.println(millis()); // millis start reading
unsigned long timeout = millis();
while (client.available()==0){
if (millis()-timeout >1000){
Serial.println(">>> Client Timeout !");
client.stop();
}
}
Serial.println(millis()); // millis get the data stream
while (client.available()){
String line = client.readStringUntil('\r');
Serial.println(String(millis()) + " >>> "+line); // millis get each
// data line
}
delay(10);
client.stop(); // Closing connection to server
Serial.println("done");
}
delay(30000);
}
And is there any other way to fetch the data from the server to an ESP8266 module?
Thank you,
-Danny-
[SOLVED]: I need to set the Timeout parameter for the readStringUntil() function.
readStringUntil() is a blocking function. I will wait until it sees a char ('\r' in your case) or the timeout exceeds.
Just need to add client.setTimeout as:
WifiClient client;
client.setTimeout(10);
Still not sure if this practice is recommended, and how smaller should I go to for timeout?
Another solution would be using freeRTOS, and put the webserver in a separate task.
Then it can wait forever until a request is coming in.

serial.println(String) prints "#" instead of the string

i'm just trying to recieve a string using a serial and to send this string back. So when i send a string to an arduino over a serial the arduino should automaticly send this string back.
i created this code:
String test;
void setup(){
Serial.begin(9600);
Serial.setTimeout(2);
test = "null";
}
void loop(){
if(Serial.available()){
test = Serial.readString();
}
Serial.println(test);
}
I guess it is not that difficult to understand. However now the arduino will always print a "#" instead of the variable test. My connected serial device is a bluetooth modul. (hc-06)
What did i do wrong?
Thank you!
(i also ran this code in the arduino emulator 123D Circuits. There it worked just fine)
You need change your code. Move println into if statement.
Try increase timeout interval, 2ms is not enough, good value (at 9600) lies above 10ms. Theoretically timeout should be at least 3.5 characters long and for current speed this equals ~0,4 ms. But in practice higher values are used.
String test;
void setup(){
Serial.begin(9600);
Serial.setTimeout(10);// or more
test = "null";
}
void loop(){
if(Serial.available()){
test = Serial.readString();
Serial.println(test);// moved into if
}
}
Update: Another simple solution to return characters back looks like:
void loop(){
if(Serial.available()) Serial.write(Serial.read());
}
Update 2: Had similar issue with BLE module HM10 (clone, not official). It was sending about 15 dummy bytes prior to any array. And i didn't solve it. But if weired bytes always the same you can make a simple trick using String.remove():
if(Serial.available()){
test = Serial.readString();
test.remove(0,5);
// test.remove - add code to remove last character
Serial.println(test);
}
Also try another terminal.

Serial.print only once Arduino

I have a light sensor that prints the value of its input to the Serial monitor. It's pretty much a trip wire but when an object is in its way, it prints the value every 1 millisecond. If I add a delay it won;t trigger the second sensor until the delay is done. How would I get it to only print once, without any disturbance or interference with the other sensors?
void loop() {
if (analogRead(sensor1) == 0) {
timer.start ();
tStop = false;
//Serial.println (timer.elapsed());
Serial.println ("Start Time = 0");
}
This is quite an interesting problem, in the normal world of computers we would solve this via threading. However as you are running without an OS we have to do one of two things, implement coroutines (fake threading without an OS) or use asynchronous code and interrupts.
My understanding is that you print something when an object first comes into the way of your sensor, as the arduino uno as opposed to the due is not easy to implement coroutines on we shall try the interrupt route.
First you will likely be interested in this library http://playground.arduino.cc/Code/Timer1
It allows you to add an interrupt service routine to run on a timer. Use the attachInterrupt(function, period) function in the library for this.
In your interrupt service routine you will want to check the sensor, set a variable to say how long ago since it was last triggered and print the message if appropriate. This means your main loop is completely free to run other code and will not block your other sensors.
For example:
void TimFun()
{
static int LastRead;
if(LastRead && (0 == analogRead(sensor1))
{
Serial.println("SensorTrip");
}
LastRead = analogRead(sensor1);
}
void loop()
{
// Do other stuff here
}
void setup()
{
Timer1.initialize(100000);
Timer1.attachInterrupt(TimFun);
// Rest of setup Here
}
I managed to make an int before the void setup and then used a while loop. with in the if statement.
int i = 1;
if (analogRead(sensor1) == 0) {
timer.start ();
tStop = false;
while (i == 1) {
Serial.println ("Start Time = 0");
i++;
}
}
You probably should use an if instead of a while loop that will never execute more than once.
bool tripped = false;
void setup(){
//setup stuff here
}
void loop() {
if ( analogRead(sensor1) == 0 )
{
timer.start ();
tStop = false;
if ( tripped == false )
{
Serial.println ("Start Time = 0");
tripped = true;
}
}
}

How do I send async data via libwebsocket?

I am using warmcat's libwebsocket C library for a small websocket server. I have the examples up and working and can send data in response to receiving data from the websocket (such as echoing back the reversed bytes sent). However, I haven't been able to figure out how to send data asynchronously from the server without already being in the libwebsocket callback function. I need the wsi pointer to pass to libwebsocket_write() but don't see how to get the pointer except while in the callback.
I've read that libwebsocket is not thread safe, as my own experience seems to confirm.
In libwebsockets.c:line2483, I read that "The protocol callback functions are [...] called periodically to allow async transmission." This is exactly what I want but I haven't observed this happening in my own code and haven't found any way to "turn it on".
Suggestions on how to write asynchronously to the websocket I am serving?
Code for libwebsocket: http://git.warmcat.com/cgi-bin/cgit/libwebsockets/
Example of use: http://martinsikora.com/libwebsockets-simple-websocket-server
I haven't found a super clean way of doing that. What I would suggest is to register a callback on the event that server can write to the client and then check if there's asynchronous work to send there. the LWS_CALLBACK_SERVER_WRITEABLE event is just that, and if you call libwebsocket_callback_on_writable(context, wsi); from within your callback it'll be periodically called.
So, something like this:
static int callback_myprotocol(struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason,
void *user, void *in, size_t len)
{
SendState *ss = (SendState*)user;
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
printf("connection established\n");
// get the ball rolling
libwebsocket_callback_on_writable(context, wsi);
break;
case LWS_CALLBACK_SERVER_WRITEABLE: {
if (!work_to_be_done) {
// schedule ourselves to run next tick anyway
libwebsocket_callback_on_writable(context, wsi);
return 0;
}
// send our asynchronous message
libwebsocket_write(wsi, buf, size, flag);
// and schedule ourselves again
libwebsocket_callback_on_writable(context, wsi);
break;
}
default:
break;
}
return 0;
}
I adapted this from the test-fraggle.c example; the above is roughly what that example does to send messages in smaller chunks.

Resources