The ESP32 is a wonderfully versatile tool. As it has WLAN and Bluetooth on chip by default, as well as many sensors, and it still can be programmed easily with the Arduino IDE, there are few things it cannot do. Its ability to take on MicroPython makes it even more versatile, for those who can write Python programs. In this episode, I will be using the Heltec WiFi OLED Kit, which is available for less than 10 euros. It is a very nice ESP version, since it has a tiny OLED display that can be easily controlled to show what’s happening. If you want to see another project I made with this, namely an analog-digital network-based clock, see it on Github.
I have for some time been wondering how to connect the ESP32 and an Android phone. This could have multiple application areas, from passing sensor values to a phone app to controlling some mechanical device, like a car.
The only problem was that I had no experience on writing apps on Android.
Then again, what is not known already can always be learned. At first I had a look at Thunkable and MIT App Inventor, both of which are really nice tools for making apps on Android. Especially Thunkable can be valuable for beginners who want to learn the basics, because coding is done via code blocks and such a visual approach can significantly lessen the effort.
However, using the UDP system turned out to be a little problem. Neither of these app generating tools could manage UDP communication. For Bluetooth and a variety of other protocols and mechanisms, they both have support, but not UDP. Therefore I had to bite the bullet and learn the basics of Android Studio.
UDP, or User Datagram Protocol, is a very light-weight way of communicating between two devices. The user can set the messages to be sent as he/she pleases, and they are passed very fast from point to point. The price you pay for such ease of use is that there is no error checking at either end, so data packets may or may not wind up where they were meant to go, and no receipt is sent.
Most of the time this doesn’t matter at all, but for secure communications you may be better off looking at something like MQTT, or Message Queue Telemetry Transport. It’s more cumbersome to set up, however, as it requires a message broker to be set up somewhere on the Internet. It does provide lots of features too, so it is not a bad choice for a tool in any matter.
UDP in action
UDP is one of the oldest protocols still around in the Internet. It doesn’t set up a connection between devices like the prevalent TCP protocol does with handshakes and all, it just spouts the datagrams towards IP addresses, using also a port number for targeting. For any sort of secure communications, UDP is not your top choice. It’s geared towards speedy delivery, simple as that.
But when you are just controlling a little device, it doesn’t matter. The good part is that since you can decide whether to send text or hexadecimal data, you can configure the communication to suit your needs. UDP can be set to broadcast a message to a bunch of devices too, so that all devices on the subnet receive it, or it can be used for multicasting, where a datagram packet is routed through a set of devices.
The protocols all rely on sockets. These are interfaces that can be lined to pass messages through, so when you want to use any protocol, the device must have sockets available both for sending and for listening. They are usually brought in via libraries or header files, and they can then be used constantly:
import java.net.DatagramPacket
import java.net.DatagramSocket
In the code below you can see the socket being instanced from the class of DatagramSocket. The packet too is instanced from a class.
The sending routine in this case, when I have an Android app doing the sending, looks like this (I am using Kotlin as the language here):
fun sendUDP(messageStr: String) { val policy = StrictMode.ThreadPolicy.Builder().permitAll().build() StrictMode.setThreadPolicy(policy) try { //Open a port to send the package val socket = DatagramSocket() socket.broadcast = true val sendData = messageStr.toByteArray() val sendPacket = DatagramPacket(sendData, sendData.size, InetAddress.getByName(mySettings.RemoteHost), mySettings.RemotePort) socket.send(sendPacket) } catch (e: IOException) { // Log.e(FragmentActivity.TAG, "IOException: " + e.message) } }
In this function, a message string is supplied to the function, converted to a byte array, which is then attached to a packet in the val sendPacket clause. The InetAddress attaches the target IP and port to the operation, and the actual sending is done by the socket.send(sendPacket) function. Of course there are other parts in the app that are important too, but I don’t claim any actual understanding in writing this UDP app. I took parts for it from various tutorials and somehow managed to cobble together a working solution.
In the receiving end, then, the ESP32 is set up thus in C:
First the libraries at the start:
#include "WiFi.h" #include "AsyncUDP.h" int localPort = 1234; AsyncUDP udp; //this creates an UDP object for later use ... //when there is a packet in the queue: if (udp.listen(localPort)) { //delay a little so as not to crash the queue delay(100); //then deal with the packet udp.onPacket([](AsyncUDPPacket packet) { char buf[packet.length() + 1] = {}; memcpy(buf, packet.data(), packet.length()); String s = String(buf); //in this app, I am sending two integers //separated by a comma, so they are dealt with here int udpSplit = s.indexOf(","); udpSpeed = s.substring(0, udpSplit); udpDir = s.substring(udpSplit + 1, packet.length()); driveSpeed = udpSpeed.toInt(); driveDir = udpDir.toInt(); Serial.println(driveSpeed); //then run the motor and the servo runMotor (driveDir, driveSpeed); delay(100); }); }
So, in essence, the ESP32 is just listening on the set local port, in my example, 1234. This is hard coded – it can’t be changed without recompiling and re-flashing the ESP32. The Android app is more flexible – in it, you can change the IP address and the port of the target device, in case you have more than one such device available.
This video gives you an idea of how the user interface works:
This video shows you what happens with the devices:
What follows next on Part 2 is the ESP32 end of the story. It makes sense to first get it done and operating, because you can use generic apps like UDP Sender to test your ESP32, then work on the Android project to get that thing running too.