ArduinoHTTPClient websocket undocumented max message size? - websocket

I have an Arduino MKRNB 1500 (with LTE-M network capability).
My code uses a websocket to upload messages to a server. The messages are uploaded every second or so of around 800-1000 bytes. My websocket server accepts these messages (i have tried with a browser client). But the ArduinoHTTPClient library WebSocketClient refuses to send messages that are over 128 bytes. The Arduino just hangs from that point onwards.
Because of the network latency, this means i cannot send more than around 600 bytes a second.
This limitation seems arbitrary, and is not documented as far as i have seen. It can be easily reproduced using the following code. Sending smaller messages more frequently is not an option because the network latency of LTE-M is around 150ms.
How can i send larger messages?
#include <ArduinoHttpClient.h>
#include <MKRNB.h> //For LTE-M or NB-IOT connections
#include "arduino_secrets.h"
// initialize the LTE-M library instance
NBClient nbClient;
GPRS gprs;
NB nbAccess;
char server[] = "echo.websocket.org"; // server address
const char PINNUMBER[] = "0000"; // = SIM SECRET_PINNUMBER;
int port = 80; // port 80 is the default for HTTP
WebSocketClient client = WebSocketClient(nbClient, server, port);
int count = 120;
void setup() {
Serial.begin(9600);
// LTE-M connection
Serial.println(F("Connecting to LTE-M network"));
boolean connected = false;
while (!connected) {
if ((nbAccess.begin(PINNUMBER) == NB_READY) &&
(gprs.attachGPRS() == GPRS_READY)) {
connected = true;
} else {
Serial.println("Not connected");
delay(1000);
}
}
}
void loop() {
Serial.println("starting WebSocket client");
client.begin();
while (client.connected() and count <= 1000) {
Serial.print("Sending hello ");
Serial.println(count);
// send a hello #
client.beginMessage(TYPE_TEXT);
client.print(count);
client.print(": ");
int i = 0;
while (i<= count){
client.print("X");
i++;
}
client.endMessage();
// increment count for next message
count++;
// check if a message is available to be received
int messageSize = client.parseMessage();
if (messageSize > 0) {
Serial.println("Received a message:");
Serial.println(client.readString());
}
delay(1000);
}
Serial.println("disconnected");
}

After examining the ArduinoHttpClient library, it turns out the WebSocketClient.h file has a limited buffer defined on line 89:
uint8_t iTxBuffer[128];
i changed that to
uint8_t iTxBuffer[4096];
And the problem was resolved.

custom size must be supported soon.
https://github.com/arduino-libraries/ArduinoHttpClient/issues/68

Related

MQTT Websocket Arduino Uno with SIM7600

I'm created a program to connect an Arduino Uno to a MQTT broker.
With the code that you can see below, I can connect to the broker, for testing I use HiveMQ with a plain TCP connection, everything go more or less well.
But for the final broker I'm going to use Websockets secure (wss) and I cant find how to do it.
I read that "I should wrap the client with the websocket", like is made here, the problem is that all the libraries I found use WiFiClientSecure.h
Do you know any way to do this? Any library or example to use a GSM board to connect using websocket to MQTT?
//DEFINITION
#define TINY_GSM_MODEM_SIM7600 //have to be before the include
#define Terminal Serial
#define SIM7600RX_PIN 2
#define SIM7600TX_PIN 3
#define SIM7600PWR_PIN 5
//GPRS credentials
const char apn[] = "";
const char gprsUser[] = "";
const char gprsPass[] = "";
//MQTT
const char* broker = "broker.hivemq.com";
const char* topic = "/testPablo/init";
const char* topiclstWill = "/testPablo/last";
const int mqttPort = 1883;
uint32_t lastReconnectAttempt = 0;
//INCLUDE LIBRARIES
#include <SoftwareSerial.h>
#include <TinyGsmClient.h>
#include <PubSubClient.h>
//VARIABLE
SoftwareSerial SIM7600(SIM7600RX_PIN, SIM7600TX_PIN);
TinyGsm modem(SIM7600);
TinyGsmClient client(modem);
PubSubClient mqtt(client);
//Function
void activateSIM7600(){
Terminal.println("Starting 4G Module...");
pinMode(SIM7600PWR_PIN, OUTPUT);
digitalWrite(SIM7600PWR_PIN,HIGH);
delay(15000); //wait SIM7600 to start
Terminal.println("wait...");
}
bool connectNetwork(){
Terminal.print("Connecting to network...");
modem.gprsConnect(apn, gprsUser, gprsPass);
Terminal.print("Waiting for network...");
if (!modem.waitForNetwork()) {
Terminal.println(" fail");
delay(5000);
return false;
}
Terminal.println(" success");
if (modem.isNetworkConnected()) {
Terminal.println("Network connected");
}
}
void configureSIM7600(){
SIM7600.begin(115200);
modem.init();
String modemInfo = modem.getModemInfo();
Terminal.print("Modem Info: ");
Terminal.println(modemInfo);
connectNetwork();
}
void mqttCallback(char* topic, byte* payload, unsigned int len) {
Terminal.print("Message arrived [");
}
void configureMQTT(){
// MQTT Broker setup
mqtt.setServer(broker, 1883);
mqtt.setCallback(mqttCallback);
}
boolean mqttConnect() {
Terminal.print("Connecting to ");
Terminal.print(broker);
// Connect to MQTT Broker
boolean status = mqtt.connect("GsmClientTest");
//authenticate MQTT:
//boolean status = mqtt.connect("GsmClientName", "mqtt_user", "mqtt_pass");
if (status == false) {
Terminal.println(" fail");
return false;
}
Terminal.println(" success");
mqtt.publish(topic, "GsmClientTest started");
//mqtt.subscribe(topicLed);
return mqtt.connected();
}
void setup() {
Terminal.begin(115200);
delay(10);
activateSIM7600();
configureSIM7600();
configureMQTT();
Terminal.println("Finish configuration");
}
void loop() {
if (!mqtt.connected()) {
Terminal.println("=== MQTT NOT CONNECTED ===");
uint32_t t = millis(); // Reconnect every 10 seconds
if (t - lastReconnectAttempt > 10000L) {
lastReconnectAttempt = t;
if (mqttConnect()) {
lastReconnectAttempt = 0;
}
}
delay(100);
return;
}
mqtt.loop();
}
You want an Arduino to connect to a broker, in most cases this is done by using ports 1883 or 8883 (secured).
WebSockets are needed if you want to connect to a web client such as a browser and this is done in most cases over ports 8080, 8081 an alike.
If I've understood the README for the library you've linked to you need to use the TinyGsmClient instance instead of the WifiClientSecure
Something like:
TinyGsmClient client(modem);
WebSocketClient250 wsClient(client, host, port);
WebSocketStreamClient wsStreamClient(wsClient, path);
PubSubClient mqtt(wsStreamClient);
This should work for the unsecured version

nodeMCU connect refused with XMAPP localhost

My project is about using nodeMCU measure value from sensor DHT11 and sent value to database Mysql. I use xampp for server. I cannot sent value to database.
nodeMCU can read value and sent value.But HTTP GET is fail.And return connect refused. I think maybe have problems with port for listening.
this is my code
#include <Arduino.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include "DHT.h"
#define DHTPIN 2 // what digital pin the DHT22 is conected to
#define DHTTYPE DHT11 // there are multiple kinds of DHT sensors
DHT dht(DHTPIN, DHTTYPE);
ESP8266WiFiMulti WiFiMulti;
const char* ssid = "something";
const char* password = "something";
int EP =5;
void setup() {
Serial.begin(115200);
pinMode(EP, INPUT);
for (uint8_t t = 4; t > 0; t--) {
Serial.printf("[SETUP] WAIT %d...\n", t);
Serial.flush();
delay(1000);
}
WiFiMulti.addAP(ssid, password); // ssid , password
randomSeed(50);
}
int timeSinceLastRead = 0;
void loop() {
if ((WiFiMulti.run() == WL_CONNECTED)) {
HTTPClient http;
float temp = dht.readTemperature();
float humi = dht.readHumidity();
long meas =TP_init();
Serial.println(WiFi.localIP());
//int temp = random(25,35);
String url = "localhost:8012/add2.php?temp="+String(temp)+"&humi="+String(humi)+"&meas=0";
Serial.println(url);
http.begin(url); //HTTP
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}
delay(3000);
}
long TP_init(){
delay(10);
long meas=pulseIn (EP, HIGH); //wait for the pin to get HIGH and returns measurement
return meas;
}
I changed Apache port from 80 to 8012
I use PHPMyadmin for store database . File php's name add2.php for insert value from sensor DHT11
enter image description here
This is result from serial port.
String url = "localhost should be replaced with String url = "<IP-address-of-your-webserver> as the webserver clearly isn't running on the ESP8266.

Arduino callback stops working when there is a new connection to MQTT broker

First time posting on here, I am hoping somebody will be able to give me some help with an annoying problem I a having.
I am trying to control an Arduino through MQTT with websockets this all works fine until a new connection to the broker is made, the Arduino then does not respond to the callbacks.
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
// Update these with values suitable for your network.
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
IPAddress ip(192, 168, 0, 123);
const char* server = "192.168.0.30";
char message_buff[100];
// defines and variable for sensor/control mode
#define MODE_OFF 0 // not sensing light, LED off
#define MODE_ON 1 // not sensing light, LED on
#define MODE_SENSE 2 // sensing light, LED controlled by software
int senseMode = 0;
unsigned long time;
EthernetClient ethClient;
PubSubClient mqttClient(ethClient);
void callback(char* topic, byte* payload, unsigned int length) {
int i = 0;
for (int i=0;i<length;i++) {
message_buff[i] = payload[i];
}
String msgString = String(message_buff);
Serial.println("Payload: " + msgString);
if (msgString.equals("onn")){
senseMode = MODE_ON;
}else if(msgString.equals("off")){
senseMode = MODE_OFF;
}
}
void reconnect() {
// Loop until we're reconnected
while (!mqttClient.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (mqttClient.connect("arduinoClient")) {
Serial.println("connected");
// Once connected, publish an announcement...
mqttClient.publish("test","AR1 Connected");
// ... and resubscribe
mqttClient.subscribe("test");
} else {
Serial.print("failed, rc=");
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup()
{
Serial.begin(9600);
pinMode(7, OUTPUT);
mqttClient.setServer(server, 1883);
mqttClient.setCallback(callback);
Ethernet.begin(mac, ip);
// Allow the hardware to sort itself out
delay(1500);
}
void loop()
{
if (!mqttClient.connected()) {
reconnect();
}
switch (senseMode) {
case MODE_OFF:
// light should be off
digitalWrite(7, LOW);
break;
case MODE_ON:
// light should be on
digitalWrite(7, HIGH);
break;
}
mqttClient.loop();
}
Outputs:
Attempting MQTT connection...failed, rc= try again in 5 seconds
Attempting MQTT connection...connected
Payload: onn
Payload: off
Payload: JTPA.CONNECTED
Payload: onnA.CONNECTED
Payload: offA.CONNECTED
Any help would be much appreciated.
Thanks.
As thrashed out in the comments the problem here is that you are using the same MQTT client ID for all instances. This will not work as every client needs a unique id and the broker will disconnect duplicates.
You need a way to generate a unique id, this question on the Arduino site has some hints on how to do this.

RPi + ESP8266 stability issues

I was recently working on a home automation project which has finally come to end and I am thinking to install it in my home. First of all, I would like to brief you with the basic architecture.
I am using a Raspberry Pi 3 as the central controller node which is running Node-Red and its Mosca palette. Currently, there are 5 ESP-01 in the project. Four of them are wired up with a relay and the remaining ESP is wired up with a DHT11 temperature sensor. Almost everything is running good but I am facing some stability issues, like, when I recycle the power the ESP-01 doesn't run the program. Serial monitor stays blank. However, when I disconnect the GPIO2 from the relay and then power up the ESP. The program begins. So, I have to pull out the GPIO2 then power up the ESP then connect the GPIO2 with the relay on every power recycle. Another problem which I am facing is sometimes the ESP crashes automatically. It sometimes prints out fatal exception(0) or soft wdt reset even though I have added a watchdog timer.
Here is the code for one of the client ESP:
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
const char* ssid = "........";
const char* password = ".........";
const int led = 13;
const char* mqtt_server = "192.168.1.8";
WiFiClient espClient;
PubSubClient client(espClient);
const int ledGPIO2 = 2;
void setup_wifi() {
int i;
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("WIFI BEGUN");
while (WiFi.status() != WL_CONNECTED) {
ESP.wdtFeed();
delay(500);
i++;
if ((i&0x01)==0){
digitalWrite(led, 0);
} else {
digitalWrite(led, 1); // led should start blinking at .5 seconds
}
Serial.print(".");
if (i>1000) break; // get out after 50 seconds
if (i==1000){
}
Serial.print(".");
Serial.println("");
Serial.print("WiFi connected - ESP IP address: ");
Serial.println(WiFi.localIP());
}
}
void callback(String topic, byte* message, unsigned int length) {
Serial.print("Message arrived on topic: ");
Serial.print(topic);
Serial.print(". Message: ");
String messageTemp;
for (int i = 0; i < length; i++) {
Serial.print((char)message[i]);
messageTemp += (char)message[i];
}
Serial.println();
if(topic=="Lamp1"){
Serial.print("Changing GPIO 2 to ");
if(messageTemp == "on"){
digitalWrite(ledGPIO2, HIGH);
Serial.print("On");
}
else if(messageTemp == "off"){
digitalWrite(ledGPIO2, LOW);
Serial.print("Off");
}
}
Serial.println();
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect("ESP8266Client")) {
Serial.println("connected");
client.subscribe("Lamp1");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup() {
pinMode(ledGPIO2, OUTPUT);
digitalWrite(ledGPIO2, true);
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}
void loop() {
if (!client.connected()) {
reconnect();
}
if(!client.loop())
client.connect("ESP8266Client");
}
Also, I have been thinking for an efficient power supply for ESP. Batteries cannot be reliable for long-term and powering up via an adapter would be unfeasible as the module is going to be mounted on the wall. Moreover, ac to dc converter was something which seems to be a decent way for power supply.
Vcc - 3.3V
CH_PD - 3.3V
Tx - Tx (Arduino)
Rx - Rx (Arduino)
GPIO0 - GND (while uploading the sketch)/ 3.3V
GND - GND
I am not using capacitors or resistors. I am getting a 5V supply from Arduino which is regulated to 3.3V using LD33V voltage regulator.
Any suggestions would be appreciated. Thank You!!

libwebsockets write to all active connections after receive

I am toying around with a libwebsockets tutorial trying to make it such that, after it receives a message from a connection over a given protocol, it sends a response to all active connections implementing that protocol. I have used the function libwebsocket_callback_all_protocol but it is not doing what I think it should do from its name (I'm not quite sure what it does from the documentation).
The goal is to have two webpages open and, when info is sent from one, the result will be relayed to both. Below is my code - you'll see that libwebsocket_callback_all_protocol is called in main (which currently does nothing, I think....) :
#include <stdio.h>
#include <stdlib.h>
#include <libwebsockets.h>
#include <string.h>
static int callback_http(struct libwebsocket_context * this,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason, void *user,
void *in, size_t len)
{
return 0;
}
static int callback_dumb_increment(struct libwebsocket_context * this,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason,
void *user, void *in, size_t len)
{
switch (reason) {
case LWS_CALLBACK_ESTABLISHED: // just log message that someone is connecting
printf("connection established\n");
break;
case LWS_CALLBACK_RECEIVE: { // the funny part
// create a buffer to hold our response
// it has to have some pre and post padding. You don't need to care
// what comes there, libwebsockets will do everything for you. For more info see
// http://git.warmcat.com/cgi-bin/cgit/libwebsockets/tree/lib/libwebsockets.h#n597
unsigned char *buf = (unsigned char*) malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
LWS_SEND_BUFFER_POST_PADDING);
int i;
// pointer to `void *in` holds the incomming request
// we're just going to put it in reverse order and put it in `buf` with
// correct offset. `len` holds length of the request.
for (i=0; i < len; i++) {
buf[LWS_SEND_BUFFER_PRE_PADDING + (len - 1) - i ] = ((char *) in)[i];
}
// log what we recieved and what we're going to send as a response.
// that disco syntax `%.*s` is used to print just a part of our buffer
// http://stackoverflow.com/questions/5189071/print-part-of-char-array
printf("received data: %s, replying: %.*s\n", (char *) in, (int) len,
buf + LWS_SEND_BUFFER_PRE_PADDING);
// send response
// just notice that we have to tell where exactly our response starts. That's
// why there's `buf[LWS_SEND_BUFFER_PRE_PADDING]` and how long it is.
// we know that our response has the same length as request because
// it's the same message in reverse order.
libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], len, LWS_WRITE_TEXT);
// release memory back into the wild
free(buf);
break;
}
default:
break;
}
return 0;
}
static struct libwebsocket_protocols protocols[] = {
/* first protocol must always be HTTP handler */
{
"http-only", // name
callback_http, // callback
0, // per_session_data_size
0
},
{
"dumb-increment-protocol", // protocol name - very important!
callback_dumb_increment, // callback
0, // we don't use any per session data
0
},
{
NULL, NULL, 0, 0 /* End of list */
}
};
int main(void) {
// server url will be http://localhost:9000
int port = 9000;
const char *interface = NULL;
struct libwebsocket_context *context;
// we're not using ssl
const char *cert_path = NULL;
const char *key_path = NULL;
// no special options
int opts = 0;
// create libwebsocket context representing this server
struct lws_context_creation_info info;
memset(&info, 0, sizeof info);
info.port = port;
info.iface = interface;
info.protocols = protocols;
info.extensions = libwebsocket_get_internal_extensions();
info.ssl_cert_filepath = cert_path;
info.ssl_private_key_filepath = key_path;
info.gid = -1;
info.uid = -1;
info.options = opts;
info.user = NULL;
info.ka_time = 0;
info.ka_probes = 0;
info.ka_interval = 0;
/*context = libwebsocket_create_context(port, interface, protocols,
libwebsocket_get_internal_extensions,
cert_path, key_path, -1, -1, opts);
*/
context = libwebsocket_create_context(&info);
if (context == NULL) {
fprintf(stderr, "libwebsocket init failed\n");
return -1;
}
libwebsocket_callback_all_protocol(&protocols[1], LWS_CALLBACK_RECEIVE);
printf("starting server...\n");
// infinite loop, to end this server send SIGTERM. (CTRL+C)
while (1) {
libwebsocket_service(context, 50);
// libwebsocket_service will process all waiting events with their
// callback functions and then wait 50 ms.
// (this is a single threaded webserver and this will keep our server
// from generating load while there are not requests to process)
}
libwebsocket_context_destroy(context);
return 0;
}
I had the same problem, the libwebsocket_write on LWS_CALLBACK_ESTABLISHED generate some random segfault so using the mail list the libwebsockets developer Andy Green instructed me the correct way is to use libwebsocket_callback_on_writable_all_protocol, the file test-server/test-server.c in library source code shows sample of use.
libwebsocket_callback_on_writable_all_protocol(libwebsockets_get_protocol(wsi))
It worked very well to notify all instances, but it only call the write method in all connected instances, it do not define the data to send. You need to manage the data yourself. The sample source file test-server.c show a sample ring buffer to do it.
http://ml.libwebsockets.org/pipermail/libwebsockets/2015-January/001580.html
Hope it helps.
From what I can quickly grab from the documentation, in order to send a message to all clients, what you should do is store somewhere (in a vector, a hashmap, an array, whatever) the struct libwebsocket * wsi that you have access when your clients connect.
Then when you receive a message and want to broadcast it, simply call libwebsocket_write on all wsi * instances.
That's what I'd do, anyway.

Resources