|
|
Here, I'll walk you through the source code of the [Proprietary Lights](What-am-I-looking-at-here#proprietarylight0-2), running on ESP32 microcontrollers.
|
|
|
|
|
|
The discussed file can be found [here](https://gitlab.hrz.tu-chemnitz.de/s6869070--tu-dresden.de/vws-spielwiese/-/blob/b1802bdd98650bdb666f03483f1e13c7f99bf4b8/esp/ProprietaryLight/ProprietaryLight.ino)
|
|
|
|
|
|
```
|
|
|
#define ID "ProprietaryLight0"
|
|
|
```
|
|
|
Here, we define the ID of the Proprietary light to be programmed. Valid values are `ProprietaryLight0`, `ProprietaryLight0`, and `ProprietaryLight0` per default setup.
|
|
|
This definition is used to identify the light inside the [Proprietary Protocol](Proprietary-Protocol).
|
|
|
|
|
|
```
|
|
|
#define RED_LED 4
|
|
|
#define GREEN_LED 2
|
|
|
```
|
|
|
Here, the GPIO pins used for the red and green LEDs attached to the ESP32 Dev Board are defined. If you were to diverge from the [default wiring of the demo](TODO), you'll need to adjust the GPIO numbers here.
|
|
|
|
|
|
```
|
|
|
#define TOPIC_PREFIX "proprietaryProtocol/" ID
|
|
|
```
|
|
|
Here, the prefix for all MQTT topics used are defined. The string `proprietaryProtocol/` and the previously defined string ID are concatenated. [See here](https://stackoverflow.com/questions/5256313/c-c-macro-string-concatenation/5256500) for more detail.
|
|
|
|
|
|
```
|
|
|
#include "EspMQTTClient.h"
|
|
|
#include <ESPmDNS.h>
|
|
|
```
|
|
|
Here we include both the EspMQTTClient library, required as per the [ESP32 setup guide](Setting-up-an-ESP32) and the ESPmDNS header, which comes as part of espressif's standard library for the ESP32.
|
|
|
EspMQTTClient is used for easy of use and stability of the MQTT connection, ESPmDNS is used to resolve the local address of the [ESP32 Controller](TODO) `esp-controller.local`.
|
|
|
|
|
|
We also utilize the WiFi library, but this is included by the EspMQTTClient library, so we don't have to explicitly include that here.
|
|
|
|
|
|
```c++
|
|
|
const char* ssid = "";
|
|
|
const char* password = "";
|
|
|
```
|
|
|
Here, the SSID and password of the WiFi network are set. You'll need to set them according to the [secrets of the Demo](TODO).
|
|
|
|
|
|
```c++
|
|
|
char bufIP[20];
|
|
|
```
|
|
|
This is the definition of the buffer used to pass the later resolved IP address of the ESP32 Controller to the library making the MQTT connection.
|
|
|
The global definition of `bufIP` is neccessary, as the library expects a `const char*` of the IP address to connect to, which we can't set on compile time, as the address is resolved at runtime first.
|
|
|
The details of passing the IP is explained below.
|
|
|
|
|
|
```c++
|
|
|
EspMQTTClient client;
|
|
|
IPAddress mqttServerIP;
|
|
|
```
|
|
|
We create an empty EspMQTTClient object, which we'll later pass the resolved IP and other connection parameters to and an IPAddress object, which will contain the ESP32 Controller's IP address after resolution.
|
|
|
|
|
|
```c++
|
|
|
void setup()
|
|
|
{
|
|
|
Serial.begin(115200);
|
|
|
```
|
|
|
The `setup()` function is called once on startup of the ESP32. Here, all one-time setup happens, including the initialization of the serial interface included above. You can connect via USB, it communicates at 115200 baud.
|
|
|
|
|
|
```c++
|
|
|
WiFi.begin(ssid, password);
|
|
|
```
|
|
|
This initializes the WiFi interface and attempts connecting to the specified network
|
|
|
|
|
|
```c++
|
|
|
while (WiFi.status() != WL_CONNECTED) {
|
|
|
delay(1000);
|
|
|
Serial.println("Connecting to Wifi...");
|
|
|
}
|
|
|
```
|
|
|
Using `WiFi.status()`, one can inquire about the status of the wireless interface. Here, we'll check whether the connection to the network has been established every second while sending `Connecting to Wifi...` to the serial interface.
|
|
|
Once connection to the network is established, the loop will exit and execution will continue.
|
|
|
|
|
|
```c++
|
|
|
mqttServerIP = IPAddress(0, 0, 0, 0);
|
|
|
```
|
|
|
Initializes the `mqttServerIP` to the IP address `0.0.0.0`, which we'll later test for change to check whether or not name resolution has occurred.
|
|
|
|
|
|
```c++
|
|
|
mdns_init();
|
|
|
```
|
|
|
Initializes the mDNS resolution for the ESP32
|
|
|
|
|
|
```c++
|
|
|
while (mqttServerIP == IPAddress(0, 0, 0, 0)) {
|
|
|
mqttServerIP = MDNS.queryHost("esp-controller");
|
|
|
Serial.println("Resolving for esp-controller.local...");
|
|
|
}
|
|
|
|
|
|
Serial.print("esp-controller.local found at ");
|
|
|
Serial.println(mqttServerIP);
|
|
|
```
|
|
|
Similar to the WiFi connection, we check, whether or not the resolution of `esp-controller.local` has been successful. Once resolved, the address is sent to the serial interface.
|
|
|
|
|
|
Note, that the mDNS implementation on the ESP32 will add `.local` to addresses to be resolved via mDNS. This might cause a problem in some networks, see [this discussion](https://github.com/espressif/esp-idf/issues/2507#issuecomment-761836300). To my knowledge, this also differs from the behaviour of an ESP2866.
|
|
|
Per default configuration, this should not cause a problem in this demo though.
|
|
|
|
|
|
```c++
|
|
|
client.enableDebuggingMessages();
|
|
|
client.setMqttClientName(ID);
|
|
|
```
|
|
|
Since we're using [delayed MQTT client configuration](https://github.com/plapointe6/EspMQTTClient/blob/master/examples/DelayedMQTTClientConfiguration/DelayedMQTTClientConfiguration.ino), we're now at a point where we can setup the client, as we now know the IP address of the ESP32 Controller. Above we set the name of our MQTT client to the ID defined at the very beginning and enable debug messages to be sent to the serial interface.
|
|
|
|
|
|
```c++
|
|
|
sprintf(bufIP, "%d.%d.%d.%d", mqttServerIP[0], mqttServerIP[1], mqttServerIP[2], mqttServerIP[3]);
|
|
|
const char* b = bufIP;
|
|
|
client.setMqttServer(b, "", "", 1883);
|
|
|
```
|
|
|
Here, we use `sprintf` to fill the ip address buffer string with a string representation of the IPAddress object, we got from our name resolution prior.
|
|
|
Then a new variable of type `const char*` is assigned to point to bufIP. It is very important to not change bufIP past this point.
|
|
|
We then continue to set the MQTT connection parameters for the client. Per default setup, we don't use authentication for the broker, so login and password are both empty strings. `1883` is the port of the broker, `b` is the freshly resolved and converted IP address.
|
|
|
|
|
|
```c++
|
|
|
pinMode(RED_LED, OUTPUT);
|
|
|
pinMode(GREEN_LED, OUTPUT);
|
|
|
```
|
|
|
Lastly in our setup, we define the GPIO pins used by the LEDs to be outputs.
|
|
|
|
|
|
```c++
|
|
|
// This function is called once everything is connected (Wifi and MQTT)
|
|
|
// WARNING : YOU MUST IMPLEMENT IT IF YOU USE EspMQTTClient
|
|
|
void onConnectionEstablished()
|
|
|
{
|
|
|
```
|
|
|
This method *must* be implemented when using EspMQTTClient, as emphasized by the comment above.
|
|
|
Similar to the `setup()` method above, this is called once after connection to the MQTT broker has been established.
|
|
|
|
|
|
```c++
|
|
|
client.subscribe(TOPIC_PREFIX "/red/on", [](const String & payload) {
|
|
|
digitalWrite(RED_LED, LOW);
|
|
|
Serial.println(ID "-red switched on");
|
|
|
});
|
|
|
```
|
|
|
Once connection to the broker has been established, we'll subscribe to the various channels defined by the [Proprietary Protocol](Proprietary-Protocol). We'll only take a closer look at the first of the four nearly identical segments, as the differences should be obvious.
|
|
|
|
|
|
`client.subscribe(...)` instructs the MQTT client to subscribe to the topic given by the first parameter and execute the function given by the second.
|
|
|
Similar to the `TOPIC_PREFIX` definition before, `TOPIC_PREFIX "/red/on"` concatenates `/red/on` to the `TOPIC_PREFIX`, defined above.
|
|
|
The second argument constitutes of a function, taking the payload of the message received on the subscribed channel as argument.
|
|
|
The function will be called every time a message on the topic is received. In our case we switch the red led on, by setting the GPIO pin defined earlier `LOW`, as the LEDs used are common anode red/green LEDs and to switch them individually, we need to switch ground.
|
|
|
After that a message stating the light state is sent to the serial interface. Here, too, the concatenation is used.
|
|
|
|
|
|
```c++
|
|
|
void loop()
|
|
|
{
|
|
|
client.loop();
|
|
|
}
|
|
|
```
|
|
|
After `setup()`, this function is called endlessly. All we're doing here is instruct the MQTT client to check for new messages and do it's housekeeping (like keeping the connection alive or reestablishing it). |