esp32 reconnection after power surge - esp32

I have an esp32 as STA in my network, collecting sensor data, that publishes through my wifi AP to my home network. Everything was working fine until we had a general power surge in the neighborhood. When power came back the esp32 was out of the network. I had to recycle its power to let it re-establish connection. Is there an explanation for this? Most probably it came up much faster than the wifi or the mqtt broker did. Could this be the issue? If this is the issue, is there a way to postpone booting of the esp32 just after wifi network and mqtt is available?
Edit: I return to my original post to add some code that I used.
void setup() {
Serial.begin(115200);
delay(10);
WiFi.mode(WIFI_STA);
// Configures static IP address
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
Serial.println("STA Failed to configure");
}
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
...
client.setKeepAlive( MQTT_KEEPALIVE );
client.setServer(mqtt_server, 1883);
client.connect(aHostname);
if (!client.connected()) {
Serial.print("mqtt status in setup: ");
Serial.println(client.state());
reconnect();
} else {
client.setCallback(callback);
Serial.print("mqtt status in setup: ");
Serial.println(client.state());
client.setCallback(callback);
client.setKeepAlive( MQTT_KEEPALIVE );
}
Also if mqtt broker is lost there is a reconnection attempt in loop:
void loop(){
if (!client.connected()) {
reconnect();
}
..
}

If it's a question of ESP32 booting faster than the WiFi access point, then this has a very simple solution. Retry the WiFi connection in ESP32 until it succeeds. Then do the same with MQTT. If you think about it, the device is useless without WiFi&MQTT connections so feel free to just keep retrying forever (or until it's successful).
In your case the MQTT broker's retry in the loop() should be an obvious example of to add a similar retry for the WiFi. Since I don't use Arduino and I don't currently have the time to read the WiFi module's documentation, I'll leave the implementation to you.

Related

How to re-establish grpc bidirectional stream if internet connection is down

I am using a go client and server which is connected with grpc bidirectional stream. I need that stream to long running forever without any disconnection, but the stream disconnects within 3 minutes when the internet is down. Is there any way to stop the client from disconnecting or is there any way to reconnect automatically with the server when internet is down. If so please guide me with this. Thankyou.
When gRPC connection is closed, the state of the gRPC client connection will be
IDLE or TRANSIENT_FAILURE. Here is my example for a custom reconnect mechanism for gRPC bi-directional streaming in Go. First, I have a for loop to keep reconnecting until the gRPC server is up, which the state will become ready after calling conn.Connect().
for {
select {
case <-ctx.Done():
return false
default:
if client.Conn.GetState() != connectivity.Ready {
client.Conn.Connect()
}
// reserve a short duration (customizable) for conn to change state from idle to ready if grpc server is up
time.Sleep(500 * time.Millisecond)
if client.Conn.GetState() == connectivity.Ready {
return true
}
// define reconnect time interval (backoff) or/and reconnect attempts here
time.Sleep(2 * time.Second)
}
}
Also, a goroutine will be spawned in order to execute the reconnect tasks. After successfully reconnect, it will spawn another goroutine to listen to gRPC server.
for {
select {
case <-ctx.Done():
return
case <-reconnectCh:
if client.Conn.GetState() != connectivity.Ready && *isConnectedWebSocket {
if o.waitUntilReady(client, isConnectedWebSocket, ctx) {
err := o.generateNewProcessOrderStream(client, ctx)
if err != nil {
logger.Logger.Error("failed to establish stream connection to grpc server ...")
}
// re-listening server side streaming
go o.listenProcessOrderServerSide(client, reconnectCh, ctx, isConnectedWebSocket)
}
}
}
}
Note that the listening task is handled concurrently by another goroutine.
// listening server side streaming
go o.listenProcessOrderServerSide(client, reconnectCh, websocketCtx, isConnectedWebSocket)
You can check out my code example here. Hope this helps.

Ubuntu Mosquitto broker websocket is not working

I'm new at IoT & MQTT communication protocol. I'm trying to connect my broker which runs at Amazon Ec2 from my Vue web app via Websockets. I have started mosquitto with:
root#ip-xxx-xx-xx-xx:~# mosquitto -c /etc/mosquitto/conf.d/default.conf
1618518468: mosquitto version 1.6.7 starting
1618518468: Config loaded from /etc/mosquitto/conf.d/default.conf.
1618518468: Opening ipv4 listen socket on port 1883.
1618518468: Opening ipv6 listen socket on port 1883.
1618518468: Opening websockets listen socket on port 9001.
/etc/mosquitto/conf.d/default.conf file contains:
listener 1883
protocol mqtt
allow_anonymous true
listener 9001
protocol websockets
allow_anonymous true
My test js file is:
var mqtt = require('mqtt');
var count =0;
var client = mqtt.connect("mqtt://xx.xxx.xxx.xxx",{clientId:"mqttjs01"});
console.log("connected flag " + client.connected);
//handle incoming messages
client.on('message',function(topic, message, packet){
console.log("message is "+ message);
console.log("topic is "+ topic);
});
client.on("connect",function(){
console.log("connected "+ client.connected);
})
//handle errors
client.on("error",function(error){
console.log("Can't connect" + error);
process.exit(1)});
//publish
function publish(topic,msg,options){
console.log("publishing",msg);
if (client.connected == true){
client.publish(topic,msg,options);
}
count+=1;
if (count==2) //ens script
clearTimeout(timer_id); //stop timer
client.end();
}
//////////////
var options={
retain:true,
qos:1};
var topic="acs";
var message="test message";
var topic_list=["topic2","topic3","topic4"];
var topic_o={"topic22":0,"topic33":1,"topic44":1};
console.log("subscribing to topics");
client.subscribe(topic,{qos:0}); //single topic
client.subscribe(topic_list,{qos:1}); //topic list
client.subscribe(topic_o); //object
var timer_id=setInterval(function(){publish(topic,message,options);},5000);
//notice this is printed even before we connect
console.log("end of script");
But I'm getting this error:
New client connected from 176.xxx.xxx.xx as mqttjs01 (p2, c1, k60).
1618518546: Socket error on client mqttjs01, disconnecting.
I have installed libwebsockets, I have tried with various mosquitto versions. Current version is: 1.6.7.
Is there any problem with my client or broker? How can I fix this?
At the end of the publish() function the if statement is missing enclosing braces so it doesn't do what you think it does.
function publish(topic,msg,options){
console.log("publishing",msg);
if (client.connected == true){
client.publish(topic,msg,options);
}
count+=1;
if (count==2) //ens script
clearTimeout(timer_id); //stop timer
client.end();
}
Lets fix the indentation so we can see more clearly.
function publish(topic,msg,options){
console.log("publishing",msg);
if (client.connected == true){
client.publish(topic,msg,options);
}
count+=1;
if (count==2) //ens script
clearTimeout(timer_id); //stop timer
client.end();
}
As you can see client.end() will ALWAYS be called when ever publish() is called. If you only want to publish twice you need to wrap the 2 statements in the braces (this is not python where whitespace has meaning)
if (count==2) { //ens script
clearTimeout(timer_id); //stop timer
client.end();
}
You really should indent all your code properly it will make it much easier to read and to spot errors like this.
Also as #JDAllen mentioned you are not making use of the WebSocket connection, unless this code is running in the browser, where the sandbox will force it to be a WebSocket connection even if you specify mqtt:// as the schema in the URL, and you will have to include the port number to make it actually connect. e.g.
ws://xxx.xxx.xxx.xxx:9001

How to detect if RSocket connection is successfull?

I have the following program through which I can detect the connection failure i.e doBeforeRetry.
Can someone tell me how to detect the successful connection or reconnection. I want to integrate a Health Check program that monitors this connection, but I am unable to capture the event that informs the connections is successfull.
Thanks
requester = RSocketRequester.builder()
.rsocketConnector(connector -> {
connector.reconnect(Retry
.fixedDelay(Integer.MAX_VALUE,Duration.ofSeconds(1))
.doBeforeRetry(e-> System.out.println("doBeforeRetry===>"+e))
.doAfterRetry(e-> System.out.println("doAfterRetry===>"+e))
);
connector.payloadDecoder(PayloadDecoder.ZERO_COPY);
}
).dataMimeType(MediaType.APPLICATION_CBOR)
.rsocketStrategies(strategies)
.tcp("localhost", 7999);
I achieved the detection of successful connection or reconnection with the following approach.
Client Side (Connection initialization)
Mono<RSocketRequester> requester = Mono.just(RSocketRequester.builder()
.rsocketConnector(
// connector configuration goes here
)
.dataMimeType(MediaType.APPLICATION_CBOR)
.setupRoute("client-handshake")
.setupData("caller-name")
.tcp("localhost", 7999)));
One the server side
#ConnectMapping("client-handshake")
public void connect(RSocketRequester requester, #Payload String callerName) {
LOG.info("Client Connection Handshake: [{}]", callerName);
requester
.route("server-handshake")
.data("I am server")
.retrieveMono(Void.class)
.subscribe();
}
On the client side, when I receive the callback on the below method, I detect the connection is successfull.
#MessageMapping("server-handshake")
public Mono<ConsumerPreference> handshake(final String response){
LOG.info("Server Connection Handshake received : Server message [{}]", response.getCallerName());
connectionSuccess.set(true);
return Mono.empty();
}else{
throw new InitializationException("Invalid response message received from Server");
}
}
Additionally, created a application level heartbeat to ensure, the liveliness of the connection.
If you want to know if it's actually healthy, you should probably have a side task that is polling the health of the RSocket, by sending something like a custom ping protocol to your backend. You could time that and confirm that you have a healthy connection, record latencies and success/failures.

boost asio - exclusive binding to the network port

I develop server app using boost asio. App works great. What doesn't work, is the the exclusive binding to the network port.
I launch one instance of the app - it starts listening to incoming connections.
I launch one more instance - it also starts listening to incoming connections on the same port. Handler that passed to async_accept do not invoked with error as expected.
Usually I just try to acquire the port. If operation fails - port is in use. With Asio this approach does not work. How to check availability of the port?
void TcpServerFactory::acceptConnectionsOnPort(int serverPort,
boost::shared_ptr<TcpConfigServerReceiver> tcpConfig,
boost::function<void(boost::shared_ptr<TcpServer>)> onSuccessfullyConnectedHandler)
{
// todo check is port not busy
FORMATTED_LOG(this->_log, info) << "Start to accept connections on port " << serverPort;
auto endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), serverPort);
boost::shared_ptr<boost::asio::ip::tcp::acceptor> tcpAcceptor(new boost::asio::ip::tcp::acceptor(this->_ioService, endpoint));
this->acceptConnections(tcpAcceptor, tcpConfig, onSuccessfullyConnectedHandler);
}
void TcpServerFactory::acceptConnections(boost::shared_ptr<boost::asio::ip::tcp::acceptor> tcpAcceptor,
boost::shared_ptr<TcpConfigServerReceiver> tcpConfig,
boost::function<void(boost::shared_ptr<TcpServer>)> onSuccessfullyConnectedHandler)
{
boost::shared_ptr<boost::asio::ip::tcp::socket> tcpSocket(new boost::asio::ip::tcp::socket(this->_ioService));
boost::function<void(const boost::system::error_code &)> onAcceptOperationCompletedHandler =
boost::bind(&TcpServerFactory::onAcceptOperationCompleted, this->downcasted_shared_from_this<TcpServerFactory>(),
_1, tcpAcceptor, tcpSocket, tcpConfig, onSuccessfullyConnectedHandler);
tcpAcceptor.get()->async_accept(*tcpSocket, onAcceptOperationCompletedHandler);
}
void TcpServerFactory::onAcceptOperationCompleted(const boost::system::error_code & err,
boost::shared_ptr<boost::asio::ip::tcp::acceptor> tcpAcceptor,
boost::shared_ptr<boost::asio::ip::tcp::socket> tcpSocket,
boost::shared_ptr<TcpConfigServerReceiver> tcpConfig,
boost::function<void(boost::shared_ptr<TcpServer>)> onSuccessfullyConnectedHandler)
{
if (err)
{
FORMATTED_LOG(this->_log, info) << "Failed to accept connections on port " << tcpAcceptor->local_endpoint().port() << "due to error " << BOOST_ERROR_TO_STREAM(err);
return;
}
this->acceptConnections(tcpAcceptor, tcpConfig, onSuccessfullyConnectedHandler);
this->onConnectionEstablished(tcpSocket, tcpConfig, onSuccessfullyConnectedHandler);
}
Update
I tried to replace constructor of acceptor on series of commands. I expected that on tcpAcceptor->bind() exception will be raised, but that didn't happened.
// boost::shared_ptr<boost::asio::ip::tcp::acceptor> tcpAcceptor(new boost::asio::ip::tcp::acceptor(this->_ioService, endpoint));
boost::shared_ptr<boost::asio::ip::tcp::acceptor> tcpAcceptor(new boost::asio::ip::tcp::acceptor(this->_ioService));
tcpAcceptor->open(endpoint.protocol());
tcpAcceptor->set_option(boost::asio::socket_base::reuse_address(true));
tcpAcceptor->bind(endpoint);
boost::system::error_code err;
tcpAcceptor->listen(boost::asio::socket_base::max_connections, err);
reuse_address is not supposed to do that. Its meaning to avoid "wait" interval after port was freed.

Arduino Ethernet client.available() not working

I eventually want to send a GET request to my Amazon EC2 server, but for now I just need to get the GET request to work in some fashion. To do that, I am trying to send a GET request to google as a test.
I am using a Arduino Uno with an Ethernet shield connected to the internet via DHCP.
My issue is that although client.connect() seems to work, client.available doesn't work either with google or with my EC2 server. Although, if my issue lies elsewhere, please tell me.
I am able to ping google and do a Telnet GET request simulation using:
-telnet www.google.com 80
-GET /search?q=arduino HTTP/1.0
Arduino Code:
#include <SPI.h>
#include <Ethernet.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0F, 0xD2, 0xAF };
//byte mac[] = {0xf0, 0x1f, 0xaf, 0x33, 0x62, 0x2f };
IPAddress ip(192,168,1,11);
// initialize the library instance:
EthernetClient client;
//char server[] = "ec2-54-69-168-77.us-west-2.compute.amazonaws.com";
char server[] = "www.google.com";
double dummyValue = 7.5;
void setup()
{
Serial.begin(9600);
// attempt a DHCP connection:
Serial.println("Attempting to get an IP address using DHCP:");
if (!Ethernet.begin(mac)) {
// if DHCP fails, start with a hard-coded address:
Serial.println("failed to get an IP address using DHCP, trying manually");
Ethernet.begin(mac, ip);
}
Serial.print("My address:");
Serial.println(Ethernet.localIP());
}
void loop()
{
// connect to the server
for(int i = 0;i <100 ; i++) {
if (client.connect(server, 80)) {
// print to serial monitor
Serial.println("connected...");
Serial.println("ARDUINO: forming HTTP request message");
// send data the server through GET request
//client.print("GET /~sclaybon3/firstdatatest.php?reading=3 HTTP/1.0\r\n");
client.print("GET /search?q=arduino HTTP/1.0\r\n");
Serial.println("Get request");
//client.print(dummyValue);
//client.print(" HTTP/1.1");
//client.print("Host: ec2-54-69-168-77.us-west-2.compute.amazonaws.com\r\n");
client.print("Host: www.google.com\r\n");
//Serial.println("Host:www.google.com");
//client.print("Connection: close\r\n\r\n");
client.print("\r\n");
Serial.println("ARDUINO: HTTP message sent");
// give server some time to receive and store data
// before asking for response from it
delay(1000);
// get the response from the page and print it to serial port
// to ascertain that data was received properly
if(client.available())
{
Serial.println("ARDUINO: HTTP message received");
Serial.println("ARDUINO: printing received headers and script response...\n");
while(client.available())
{
char c = client.read();
Serial.print(c);
}
}
else
{
Serial.println("ARDUINO: no response received / no response received in time");
}
client.stop();
}
}
// do nothing forever after:
while(true);
}
Arduino output:
Attempting to get an IP address using DHCP:
My address:192.168.1.11
connected...
ARDUINO: forming HTTP request message
Get request
ARDUINO: HTTP message sent
ARDUINO: no response received / no response received in time

Resources