Week 11 Embedded Networking and Communications
This page documents the group assignment for week 11 of Högni, Jóhannes and Ólöf.
Group assignment:
- Send a message between two projects.
- Document your work on the group work page and reflect on your individual page what you learned.
Overview and approach
Ólöf and Jóhannes worked together at Fab Lab Ísafjörður and documented about it.
The goal was to read a sensor with one microcontroller and send the reading via a wireless connection to a second microcontroller, which would display the reading on a screen.
Jóhannes will set up a Wifi host with a XIAO ESP32-C3 and provide a wireless access point to connect to. The sensor is a One-Wire digital Temperature sensor.
Ólöf will set up a XIAO ESP32-C3 as Wifi client and connect to the access point and receive data from it. Then the readings will be displayed on an I2C LCD display.
Theory
The XIAO ESP32C3 includes a module for wireless connectivity. Here is the XIAO ESP32C3 datasheet.
There can be used different approaches to send messages between two devices via Wifi.
-
Use an existing wireless network and let the network router handle the traffic. Both devices would act as client in the network.
-
Create an access point (Soft AP) with one device (acting as host) and have the other device (client) connect to it.
We decided to go for the second option, as that allows us to set up an independent solution without having to rely on any other infrastructure.
For the Arduino IDE the Wifi.h
library includes all the important commands as well as examples for different use cases.
Also, we found this page with useful examples on Wifi communication with the XIAO ESP32-C3.
Wifi Access Point (Host)
To set up the AP, we used the example code provided by seeedstudio and adopted it to our needs.
In the serial monitor one can observe the status of the AP and if any client has connected.
Password requirements
When customizing the ssid and password of the AP, make sure to use at least eight characters for the password, otherwise it won't work.
To send data, a virtual server needs to be created to receive and answer requests. The library ESPAsyncWebServer.h
can be used for that.
The code we used looks like this:
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include <DS18B20.h>
String temp;
DS18B20 ds(D1);
AsyncWebServer server(80);
IPAddress AP_LOCAL_IP(192, 168, 4, 1);
IPAddress AP_GATEWAY_IP(192, 168, 4, 254);
IPAddress AP_NETWORK_MASK(255, 255, 255, 0);
String readTemp() {
ds.selectNext();
temp = ds.getTempC();
return temp;
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAP("ESP32C3_Johannes", "Fabweek11");
IPAddress IP = WiFi.softAPIP();
Serial.print("IP address: ");
Serial.println(IP);
}
void loop() {
Serial.print("Host Name:");
Serial.println(WiFi.softAPgetHostname());
Serial.print("Host IP:");
Serial.println(WiFi.softAPIP());
Serial.print("Host IPV6:");
#if ESP_ARDUINO_VERSION_MAJOR < 3
Serial.println(WiFi.softAPIPv6());
#else
Serial.println(WiFi.softAPlinkLocalIPv6());
#endif
Serial.print("Host SSID:");
Serial.println(WiFi.SSID());
Serial.print("Host Broadcast IP:");
Serial.println(WiFi.softAPBroadcastIP());
Serial.print("Host mac Address:");
Serial.println(WiFi.softAPmacAddress());
Serial.print("Number of Host Connections:");
Serial.println(WiFi.softAPgetStationNum());
Serial.print("Host Network ID:");
Serial.println(WiFi.softAPNetworkID());
Serial.print("Host Status:");
Serial.println(WiFi.status());
delay(1000);
Serial.print("T ");
Serial.println(temp);
server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){request->send_P(200, "text/plain", "Hi Ólöf");});
}
And here is an example of the serial monitor printing:
Wifi Client setup
This code was used for the client that was connected to the LCD screen. The ssid command gives the client the network name of the host and the next line tells the password. This way the client connects to the Wifi created by the host:
#include <WiFi.h>
const char* ssid = "ESP32C3_Johannes";
const char* password = "****";
void setup() {
Serial.begin(115200);
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void loop() {}
192.168.4.2
.
The next step was to write a code where the client would request the host to send information about the temperature.
#include <WiFi.h>
#include <HTTPClient.h>
const char* ssid = "ESP32C3_Johannes";
const char* password = "Fabweek11";
const char* serverNameTemp = "http://192.168.4.1/temperature";
String temperature; // Declare variable for temperature value
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,20,4);
void setup() {
Serial.begin(115200);
delay(10);
// We start by connecting to a WiFi network
lcd.init(); // initialize the lcd
// Print a message to the LCD.
lcd.backlight();
lcd.setCursor(0,0);
lcd.print("Connecting to ");
lcd.setCursor(0,1);
lcd.print(ssid);
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WiFi connected ");
lcd.setCursor(0, 1);
lcd.print(WiFi.localIP());
delay(5000);
}
String httpGETRequest(const char* serverName) {
WiFiClient client;
HTTPClient http;
// Your Domain name with URL path or IP address with path
http.begin(client, serverName);
// Send HTTP POST request
int httpResponseCode = http.GET();
String payload = "--";
if (httpResponseCode>0) {
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
payload = http.getString();
}
else {
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
// Free resources
http.end();
return payload;
}
void loop() {
// Check if WiFi connection is active
if(WiFi.status()==WL_CONNECTED){
temperature = httpGETRequest(serverNameTemp);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Temperature:");
lcd.setCursor(0, 1);
lcd.print(temperature);
}else{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("No connection:");
}
delay(5000);
}
Issues with the HTTP communication
No communication between client and host
The temperature did not appear on the LCD screen as intended, only the text "Temperature".
We opened the Serial Monitor in the Arduino IDE app and it displayed the text http code -1
. That means, that no HTTP connection could be established, even though the client got an IP number from the host.
We tried making changes to the code and finding the cause for this error, but nothing worked. Also our instructors couldn't see an obvious cause so we decided to postpone this task.
MQTT Communication
After Ólöf had travelled back home to Neskaupstaður from the machine week session in Ísafjörður, we still had to figure out how to solve the group assignment. Ólöf realized that the XIAO ESP32C3 microcomputer she had been using had been left behind in Ísafjörður so she had to find another XIAO ESP32C3 and solder pins to it.
Because we wanted to accomplish wireless communication - the next idea that came up was to try MQTT (because we couldn't use the same wireless network due to our geographical distance).
Jóhannes did some research and after some unsuccesful attempt he managed to find an open MQTT broker, he was able to connect to. We followed this tutorial, which used the arduino-mqtt library.
The broker suggested is: public.cloud.shiftr.io and it worked well for us.
Now the architecture is a bit different from what we had tried before.
MQTT
Both ESP32-C3 work as client and there is no host, but a MQTT broker. The broker receives messages that every client can send. The messages are organized by different topics. A client can also subscribe certain topics. In our case one client is publishing and the other is subscribing.
We created a topic /fabisa
and published and subscribed to this topic. We began by testing this example called ESP32DevelopmentBoard from the MQTT library and added our networking information and text into it:
// This example uses an ESP32 Development Board
// to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://www.shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <WiFi.h>
#include <MQTT.h>
const char ssid[] = "fablab";
const char pass[] = "******";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "public", "public")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/fabisa");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
client.begin("public.cloud.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
delay(10); // <- fixes some issues with WiFi stability
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/fabisa", "Fab Lab Austurland");
}
}
Then Ólöf opened the serial monitor and there this text was displayed:
The next step was to change this code and add to it what we needed. Together we figured out, how to display the message from the topic on the screen. These are the codes after changes:
Code for Temperature Client
// Code based on example
// by Joël Gähwiler. Changes added by Jóhannes and Ólöf
// https://github.com/256dpi/arduino-mqtt
#include <WiFi.h>
#include <MQTT.h>
#include <DS18B20.h>
String temp;
DS18B20 ds(D1);
const char ssid[] = "ssid";
const char pass[] = "****";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
String readTemp() {
ds.selectNext();
temp = ds.getTempC();
return temp;
}
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("ESP32-C3-IFJ", "public", "public")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/fabisa");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
client.begin("public.cloud.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
delay(10); // <- fixes some issues with WiFi stability
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
temp = readTemp();
client.publish("/fabisa", temp);
}
}
Jóhannes measured the temperature with the one-wire sensor and Ólöf connected the OLED screen and ran the code.
Here is a video showing the response to a heat change of the sensor in Ísafjörður live on the OLED screen in Neskaupstaður.
Code for OLED Client
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// This example uses an ESP32 Development Board
// to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://www.shiftr.io/try.
//
// based on code by Joël Gähwiler and.....
// https://github.com/256dpi/arduino-mqtt
#include <WiFi.h>
#include <MQTT.h>
const char ssid[] = "fablab";
const char pass[] = "******";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "public", "public")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/fabisa");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
display.clearDisplay();
display.setTextSize(3); // Normal 3:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0,2); // Start at top-left corner
display.println(F("Temp: "));
display.print((payload));
display.print(char(247)); //symbol for Celcius degrees
display.print(F("C"));
display.display();
delay(2000);
display.clearDisplay();
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(9600);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
client.begin("public.cloud.shiftr.io", net);
client.onMessage(messageReceived);
connect();
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
display.display();
delay(2000); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
}
void loop() {
client.loop();
delay(10); // <- fixes some issues with WiFi stability
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
}