B4R Question Deep sleep with AsyncStreams + B4XSerializator

Martin Larsen

Active Member
Licensed User
Longtime User
I am working on a remote thermometer for my greenhouse using an ESP8266-01 and DHT11.

Currently I am using AsyncStreams + B4XSerializator which is simple to work with and works well.

Now I want to put the ESP8266 in deep sleep mode so save battery and only wake up every 5 minutes or so. But the socket on the server side disconnects when the ESP goes to sleep and of course it doesn't know when to wake up. I can't rely on a timer for this as it would not be precise enough.

It is possible to let the server listen continuously even after the ESP disconnects?

My first attempt before using AsyncStreams was via a webserver on my Raspi and that would of course work. But I like the more streamlined way with AsyncStreams + B4XSerializator.
 

Martin Larsen

Active Member
Licensed User
Longtime User
The server is a simple program in B4J and B4A (B4XPages). Same result in both.

When the ESP goes to sleep, the server naturally stops receiving messages. No error occurs. I guess the socket gets disconnected, but there is no disconnected event.

The problem is when the ESP wakes up again and starts sending, the server does not receive the messages unless I click the Connect button. Then it works fine.

But since this is meant to be a monitoring system, I can't manually connect the server. I would like to just keep listening.

B4X:
Sub AppStart (Form1 As Form, Args() As String)

    socket.Initialize("socket")
    socket.Connect(IP, port, 0)
    ser.Initialize
    
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
    
End Sub

Sub Socket_Connected (Successful As Boolean)
    If Successful Then
        Log("Connected")
        If astream.IsInitialized Then astream.Close
        astream.InitializePrefix(socket.InputStream, False, socket.OutputStream, "astream")
    Else
        Log("Could not connect")
    End If
End Sub

Sub AStream_NewData (Buffer() As Byte)
    Log("New data")
    Dim data() As Object = ser.ConvertBytesToArray(Buffer)
    lblHumidity.Text = "Humidity " & data(0) & "%"
    lblTemperature.Text = "Temperature " & data(1) & " ºC"
End Sub

Sub AStream_Error
    Log("Error")
End Sub

Sub AStream_Terminated
    Log("Terminated")
End Sub

' Connect to ESP
Private Sub Button2_Click
    socket.Initialize("socket")
    socket.Connect(IP, port, 0)
End Sub
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
the server does not receive the messages unless I click the Connect button
On the ESP?

Tip: it is easier to connect, send the data and disconnect than maintaining an active connection for a long time.

If you do want to maintain an active connection then add a heartbeat method. Send "ping" messages from both sides every second and close the connection, on both sides, after 10 seconds if no ping was received.
 
Upvote 0

Martin Larsen

Active Member
Licensed User
Longtime User
On the ESP?

It's the connect button on the B4J program. It's shown at the bottom of the screenshot in this thread where it is named "Tilslut" (Danish for connect).

Tip: it is easier to connect, send the data and disconnect than maintaining an active connection for a long time.

I don't quite understand how. The two ends need to be connected at the same time. Ideally ESP takes just one reading from the DHT11 and sends it to the server and goes to sleep. It wakes up X minutes later and sends a new reading etc. But the server needs to be listening and it only starts listening when I press Connect and initiates the connection.

The problem with the ping method is that the ESP is sleeping between readings. Then it cannot send pings. The B4J server sees that the ESP is disconnected and drops the connection. When the ESP wakes up again, B4J does not automatically reconnect. But if I press the Connect button, it continues to show the readings until ESP sleeps again.

The server could try continuously try connect to the ESP but it would have to be somewhat lucky to hit the time window where ESP is awake. Of course I could let ESP be awake like 10 seconds before sleeping, that would make it easier for the B4J program to connect to it. But to maximize battery life, it should only make one reading and sleep again.

Should I post the B4R program?
 
Upvote 0

monki

Active Member
Licensed User
Longtime User
Hello Martin, Your B4J APP POSTED ABOVE is a client and not a server application, you connect from your supposed server to the ESP, if it then goes into sleep mode, the connection is interrupted. you have to run the client application on the Esp, and the server on B4J, SO EXACTLY THE DIFFERENT WAY YOU DO IT NOW
 
Upvote 0

Martin Larsen

Active Member
Licensed User
Longtime User
Hello Martin, Your B4J APP POSTED ABOVE is a client and not a server application, you connect from your supposed server to the ESP, if it then goes into sleep mode, the connection is interrupted. you have to run the client application on the Esp, and the server on B4J, SO EXACTLY THE DIFFERENT WAY YOU DO IT NOW

Interesting idea! I haven't thought of that.

It could very well be true as I have built the app upon a couple of examples. The full code of the B4R program is below, and there is indeed a server.Listen statement.
B4X:
Sub Process_Globals
    Public Serial1 As Serial
    Private wifi As ESP8266WiFi
    Private server As WiFiServerSocket
    Private astream As AsyncStreams
    Private ser As B4RSerializator
    Public DHT11pin As Pin
    Private dht11 As dht
    Dim humidity,temperature As Double ' Humidity/Temperature DHT11 readings
    Public timer As Timer
    Public Const interval As ULong = 5000
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    If wifi.Connect2("xxx", "yyy") Then
        Log("Connected to wireless network.")
        Log("My ip: ", wifi.LocalIp)
    Else
        Log("Failed to connect to network")
    End If
    
    timer.Initialize("timer_tick", interval)
    DHT11pin.Initialize(2,    DHT11pin.MODE_INPUT)
    server.Initialize(51042, "server_NewConnection")
    server.Listen
    timer.Enabled = True
    timer_tick
End Sub

Sub timer_tick
    dht11.Read11(DHT11pin.PinNumber) ' Reading the DHT11 measure
    temperature = dht11.GetTemperature ' Get temperature from readed measure
    humidity = dht11.GetHumidity       ' Get humidity from readed measure
    Log("DHT11 ","Humidity = ", humidity, " %", "  Temperature =", temperature, " ºC")
    If server.Socket.Connected Then
        astream.Write(ser.ConvertArrayToBytes(Array(humidity, temperature)))
    End If
End Sub

Sub Server_NewConnection (NewSocket As WiFiSocket)
    Log("Client connected")
    astream.InitializePrefix(NewSocket.Stream, False, "astream_NewData", "astream_Error")
End Sub

Sub AStream_Error
    Log("Error")
    server.Listen
End Sub

Sub AStream_NewData (Buffer() As Byte)
    Dim be(10) As Object
    Dim data() As Object = ser.ConvertBytesToArray(Buffer, be)
    Log("Received:")
    For Each o As Object In data
        Log(o)
        Select o
            Case "sleep"
                wifi.Disconnect
                DeepSleep(60000)
        End Select
    Next
End Sub

Private Sub DeepSleep(ms As ULong)
    RunNative("deepSleep", ms * 1000)
End Sub

#if C
void deepSleep(B4R::Object* o) {
   ESP.deepSleep(o->toULong());
}
#end if
 
Upvote 0

monki

Active Member
Licensed User
Longtime User
Hello Martin,
your code for the ESP is wrong . The ESP must to re-initiate the connection to the server after waking up. As I wrote above, it makes sense to program the ESP as a client.
If you are in sleep mode during esp and you press the connect button, no connection can be established, you'd better do it as described above then the connection to the server will be automatically re-established after each deep sleep preiode provided you call start in the app the Connect sub on.
Erel has posted an example here https://www.b4x.com/android/forum/threads/esp8266-udp-bmp180-simple-weather-station.70243/#content that works via UDP.

monki
 
Last edited:
Upvote 0

Martin Larsen

Active Member
Licensed User
Longtime User
The ESP must to re-initiate the connection to the server after waking up.

The ESP wakes up by performing a reset via GPIO 16 and starts over. So the ESP does actually reconnect. But you are probably right about the client and server being switched and I will look into that. It makes sense.

And thanks for the example using UDP! Looks interesting.
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
Something else to consider if you are trying to save battery - don't connect to wifi until after you have read the DHT11 and when you have sent the data - turn it off again then go to sleep.
 
Upvote 0

Martin Larsen

Active Member
Licensed User
Longtime User
You are right about the first suggestion. If you really want to save juice, that will help a bit.

I am not sure about turning of wifi before sleeping. Doesn't wifi switch off automatically when deep sleeping? At least the ESP disappears from my Fing app until it wakes up again.
 
Upvote 0

Martin Larsen

Active Member
Licensed User
Longtime User
you have to run the client application on the Esp, and the server on B4J, SO EXACTLY THE DIFFERENT WAY YOU DO IT NOW

You were right. I switched the programs, modified them appropriately, and after struggling with a problem that turned out to be Windows firewall blocking the requests, it worked. When ESP was the server, the firewall wasn't an issue.

Tip: it is easier to connect, send the data and disconnect than maintaining an active connection for a long time.

Correct. Now when I switched the server and client, this approach indeed works better. I think it takes some time for the server to release the connection after the ESP goes to sleep, but closing the connections in boths ends after each request works fine.


This is the right way to do it! I have turned to this approach. It is easier, more robust and works with multiple clients.

Actually, the UDP method is a lot like how a real weather station with a remote sensor works. The sensor broadcasts a one-way signal to whatever receiver happening to listen to it. You can have one or you can have many receivers, it doesn't matter.

UDP packets are also very easy to deal with server side using python, node.js or whatever if you don't want or can use B4J. I am preparing a tutorial on this matter.

Thanks for all the advice and tips in this thread!
 
Upvote 0

Martin Larsen

Active Member
Licensed User
Longtime User
Upvote 0
Top