Skip to main content

Wi-Fi

Welcome to this tutorial where we delve into the world of Wi-Fi. It's undeniable that Wi-Fi has become an essential part of our daily lives. However, have you ever pondered the mechanics behind the seemingly simple act of entering a Wi-Fi password and gaining internet access on your phone or computer? Let's unravel this mystery together.

The ESP32 is a versatile component widely employed in various projects that require Wi-Fi connectivity. We'll explore Wi-Fi utilizing the ESP32 as a Wi-Fi module.

Learning goals

  • Gain a fundamental understanding of Wi-Fi functionality.
  • Master the process of connecting the ESP32 to your Wi-Fi network.
  • Learn how to exchange data with public servers through HTTP requests.

🔸Circuit - ESP32-C3

The shield on SwiftIO Playground contains ESP32 for Wi-Fi connection.

ESP32
ESP32-C3SwiftIO Micro Pin
UARTUART1
RSTD24
GNDGND

🔸Projects

1. Joining Wi-Fi

In this project, we'll connect the ESP32 module to Wi-Fi and grab its IP address.

Project overview

  1. Set up the ESP32 module: reset it and set it to station mode.
  2. Use SSID and password for Wi-Fi connection.
  3. Display the IP address assigned to the module by your router or alternative access point (AP).

Example code

You can download the project source code here.

info

Don't forget to fill in your SSID (Wi-Fi network name) and password before downloading the code.

import SwiftIO
import MadBoard
import ESP32ATClient

let rst = DigitalOut(Id.D24, value: true)
let uart = UART(Id.UART1, baudRate: 115200)
let esp = ESP32ATClient(uart: uart, rst: rst)

do {
// If reset failed, you might need to adjust the baudrate.
try esp.reset()
print("ESP32 status: \(esp.esp32Status)")

// Only in 'Station' or 'Station+SoftAP' mode can a connection to an AP be established.
var wifiMode = ESP32ATClient.WiFiMode.station
_ = try esp.setWiFiMode(wifiMode)

wifiMode = try esp.getWiFiMode()
print("ESP32 WiFi mode: \(wifiMode)")

// Fill the SSID and password below.
try esp.joinAP(ssid: "", password: "", timeout: 20000)
print("ESP32 WiFi status: \(esp.wifiStatus)")

let ipInfo = try esp.getStationIP()
print(ipInfo)
} catch {
print("Error: \(error)")
}

while true {
sleep(ms: 1000)
}

2. Weather

If you've ever used a weather app on your phone, here's an exciting opportunity to build your own. Many weather services out there provide APIs, giving you access to their data. In this project, your goal is to send HTTP requests to a weather service, like openweathermap.org, to retrieve the latest weather updates.

Project overview

  1. Connect ESP32 to Wi-Fi.
  2. Request current weather from the server using an HTTP GET.
  3. Decode the JSON-formatted response from the server.
  4. Print the weather info.

Example code

You can download the project source code here.

import SwiftIO
import MadBoard
import ESP32ATClient
import ExtrasJSON

let rst = DigitalOut(Id.D24, value: true)
let uart = UART(Id.UART1, baudRate: 115200)
let esp = ESP32ATClient(uart: uart, rst: rst)

do {
// If reset failed, you might need to adjust the baudrate.
try esp.reset()
print("ESP32 status: \(esp.esp32Status)")

// Only in 'Station' or 'Station+SoftAP' mode can a connection to an AP be established.
var wifiMode = ESP32ATClient.WiFiMode.station
_ = try esp.setWiFiMode(wifiMode)

// Print current Wi-Fi mode.
wifiMode = try esp.getWiFiMode()
print("ESP32 WiFi mode: \(wifiMode)")

// Fill the SSID and password below.
try esp.joinAP(ssid: "", password: "", timeout: 20000)
print("ESP32 WiFi status: \(esp.wifiStatus)")

// Print the assigned IP address.
let ipInfo = try esp.getStationIP()
print(ipInfo)
} catch {
print("Error: \(error)")
}

sleep(ms: 1000)

if esp.wifiStatus == .ready {
do {
// Send request to the weather service to obtain current weather.
// Update the URL with your API key and your city name.
let response = try esp.httpGet(url: "https://api.openweathermap.org/data/2.5/weather?q=metric&q=YourCity&appid=YourAPIKey", timeout: 30000)

// Decode the JSON data and print the weather info.
let weatherInfo = try XJSONDecoder().decode(WeatherInfo.self, from: Array(response.utf8))
print("City: \(weatherInfo.cityName)")
print("Weather: \(weatherInfo.weatherConditions[0].main)")
print("Temp: \(weatherInfo.mainInfo.temp)C")
print("Humidity: \(weatherInfo.mainInfo.humidity)")
} catch let error as DecodingError {
print("JSON Decoding Error: \(error)")
} catch {
print("Http GET Error: \(error)")
}
} else {
_ = try? esp.readLine(timeout: 1000)
print("WiFi status: \(esp.wifiStatus)")
}

while true {
sleep(ms: 1000)
}

Code analysis

let response = try esp.httpGet(url: "https://api.openweathermap.org/data/2.5/weather?q=metric&q=YourCity&appid=YourAPIKey", timeout: 30000)

This statement initiates an HTTP GET request to the OpenWeatherMap API, fetching current weather data for a specified city in metric units. Make sure to replace YourCity with the actual city name and YourAPIKey with a valid OpenWeatherMap API key.

OpenWeatherMap, a widely-used weather service, offers a free account registration, providing users with an API key necessary for accessing current weather information.

let weatherInfo = try XJSONDecoder().decode(WeatherInfo.self, from: Array(response.utf8))

Decode the JSON response obtained from an HTTP request using predefined Swift structs, in this case, the WeatherInfo struct.

JSON format is commonly applied in web services for transmitting data and in software or hardware for storing settings. This format organizes information into key-value pairs, where keys serve as labels describing the data type, and values store the actual data.

Here is an example JSON data from the OpenWeatherMap.
{
"coord": {
"lon": -76.5074,
"lat": 38.3004
},
"weather": [
{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01n"
}
],
"base": "stations",
"main": {
"temp": -0.9,
"feels_like": -2.85,
"temp_min": -2.18,
"temp_max": 1.47,
"pressure": 1013,
"humidity": 89
},
"visibility": 10000,
"wind": {
"speed": 1.54,
"deg": 290
},
"clouds": {
"all": 0
},
"dt": 1707121229,
"sys": {
"type": 2,
"id": 2011802,
"country": "US",
"sunrise": 1707134876,
"sunset": 1707172323
},
"timezone": -18000,
"id": 4350049,
"name": "California",
"cod": 200
}

We'll utilize Swift Extra JSON library for data decoding. JSON, inherently key-value paired, aligns seamlessly with properties and values.

  • The predined structs in Weather.swift conform to the Decodable protocol, enabling seamless decoding from an external representation, like JSON.
  • Match JSON keys to corresponding properties in the structs. If they don't match exactly, use the CodingKeys enum to provide custom mappings.
  • Use decoder to decode JSON data into the predefined WeatherInfo struct.

3. Temperature data logger

Let's delve into how HTTP POST request works using ThingSpeak as an example. ThingSpeak is a versatile open-source Internet of Things (IoT) platform designed for collecting, storing, analyzing and visualizing data from IoT devices.

In this project, we'll focus on sending sensor readings to ThingSpeak via HTTP POST requests. By doing so, we'll be able to observe and analyze how the data changes over time.

ThingSpeak Chart

Project overview

  1. Connect ESP32 to Wi-Fi.
  2. Read temperature and humidity from sensor.
  3. Send sensor data to ThingSpeak using HTTP POST requests.
  4. Monitor temperature and humidity trends on the ThingSpeak dashboard.

Example code

You can download the project source code here.

import SwiftIO
import MadBoard
import ESP32ATClient
import SHT3x

let rst = DigitalOut(Id.D24, value: true)
let uart = UART(Id.UART1, baudRate: 115200)
let esp = ESP32ATClient(uart: uart, rst: rst)

let i2c = I2C(Id.I2C0)
let humiture = SHT3x(i2c)

do {
// If reset failed, you might need to adjust the baudrate.
try esp.reset()
print("ESP32 status: \(esp.esp32Status)")

// Only in 'Station' or 'Station+SoftAP' mode can a connection to an AP be established.
var wifiMode = ESP32ATClient.WiFiMode.station
_ = try esp.setWiFiMode(wifiMode)

wifiMode = try esp.getWiFiMode()
print("ESP32 WiFi mode: \(wifiMode)")

// Fill the SSID and password below.
try esp.joinAP(ssid: "", password: "", timeout: 20000)
print("ESP32 WiFi status: \(esp.wifiStatus)")

let ipInfo = try esp.getStationIP()
print(ipInfo)
} catch {
print("Error: \(error)")
}

while true {
sleep(ms: 30_000)
if esp.wifiStatus == .ready {
do {
// Read temperature and humidity values from the sensor.
let temp = humiture.readCelsius()
let humidity = humiture.readHumidity()
// Send the values to ThingSpeak using HTTP POST requests to visualize them.
_ = try esp.httpPost(url: "https://api.thingspeak.com/update?api_key=YourAPIKey&field1=\(temp)&field2=\(humidity)", headers: ["Content-Type: application/x-www-form-urlencoded"], timeout: 20000)
} catch {
print("Http POST Error: \(error)")
}
} else {
_ = try? esp.readLine(timeout: 1000)
print("WiFi status: \(esp.wifiStatus)")
}
}

Code analysis

_ = try esp.httpPost(url: "https://api.thingspeak.com/update?api_key=YourAPIKey&field1=\(temp)&field2=\(humidity)", headers: ["Content-Type: application/x-www-form-urlencoded"], timeout: 20000)

Send temperature and humidity data to ThingSpeak using the provided API key. Ensure to replace YourAPIKey in the URL with your unique API key, generated upon setting up a channel on ThingSpeak.

🔸API

ESP32ATClient

This library enables you to use ESP32 as a client, facilitating the sending of HTTP requests to servers.

init(uart: UART, rst: DigitalOut)

Initialize the EPS32 by specifying the UART and reset pins to which they are connected.

Parameter:

  • uart: the pins used for UART communication.
  • rst: the DigitalOut pin connected to the ESP32 reset.
func joinAP(ssid: String? = nil, password: String = "", timeout: Int = 20000, autoConnect: Bool = true) throws

Connect to the designated Wi-Fi network.

Parameter:

  • ssid: the Wi-Fi network name.
  • password: the password for the Wi-Fi network.
  • timeout: the time in milliseconds to wait for the connection to be established.
  • autoConnect: whether the ESP32 should automatically connect to the Wi-Fi network.
func reset() throws

Reset the module.

func setWiFiMode(_ newMode: WiFiMode, autoConnect: Bool = true) throws -> Bool

Set Wi-Fi mode.

Parameter:

  • newMode: the new Wi-Fi mode: none, .station, .softAP, or stationSoftAP.
  • autoConnect: whether the ESP32 should automatically connect to the Wi-Fi network.

Return value:

  • a boolean value indicating the success or failure of setting the Wi-Fi mode.
func getWiFiMode() throws -> WiFiMode

Get current Wi-Fi mode.

Return value:

  • a Wi-Fi mode: none, .station, .softAP, or stationSoftAP.
func getStationIP() throws -> [String]

Query assigned IP address after ESP32 is connected to the Wi-Fi.

Return value:

  • an array of strings representing the IP addresses.
func httpGet(url: String, timeout: Int = 5000) throws -> String

Perform an HTTP GET request to the specified URL

Parameter:

  • url: the URL of the resource you want to retrieve.
  • timeout: the time in milliseconds to wait for the response.

Return value:

  • the response or content obtained from the HTTP GET request.
func httpPost(url: String, data: String = "\r\n", headers: [String] = [], timeout: Int = 5000) throws -> String

Send data to a specified server using the HTTP POST method.

Parameter:

  • url: the server URL.
  • data: the data being sent in the request.
  • headers: HTTP request header.
  • timeout: the time in milliseconds to wait for the response.

Return value:

  • the response from the HTTP POST request.

🔸Background

Wi-Fi

You know we're all hooked on Wi-Fi for our daily dose, right? Once your computer or phone taps into Wi-Fi, you're set to surf the web. Ever wondered about the behind-the-scenes magic? The internet is this crazy intricate thing, layers upon layers of complexity. Now, don't worry, we won't drag you through a mind-numbing tech marathon. Instead, let's just take a quick peek into how Wi-Fi does its thing and get a basic idea of what's happening.

Wi-Fi Connection (Neighborhood Move-in)

  1. Connecting to Wi-Fi (Moving into a Neighborhood): Imagine your computer as a new resident looking for a cozy neighborhood. To join the community, it learns the secret handshake - the Wi-Fi network name (SSID) and password.

  2. Getting an IP address (Getting a Unique Home Address): Once inside, your computer is assigned a unique home address (IP address) by the neighborhood's hosting committee (router). This address helps neighbors (devices) recognize each other. The neighborhood is thoughtfully organized with clear boundaries (subnet mask), ensuring smooth communication among neighbors.

Web Exploration (SendingLetters in the Neighborhood)

  1. Entering the Domain Name (Craftinga Letter): Now, let's say you want to send a letter to a pen pal in a different town, just like visiting www.example.com.

  2. DNS Resolution (Using the Address Directory): Instead of memorizing complex routes, you consult a friendly address directory (DNS) that translates the pen pal's name into their actual address (IP address).

  3. Communicating with the Gateway (Neighborhood Post Office): To send the letter, you visit the neighborhood post office. The postmaster (gateway) checks if your pen pal lives in the same neighborhood (local network). If so, your letter can be hand-delivered. If the pen pal is in a different town (external network), the postmaster (gateway) knows the main roads (external routes) and forwards your letter appropriately.

  4. TCP Connection (Setting up Chat Line): Once the letter reaches the pen pal's town, a reliable chat line (TCP connection) is set up, ensuring smooth and efficient communication.

  5. HTTP Request (Requesting the Pen Pal's Story): Curious about your pen pal's tales, you ask for their latest story using the universal language of communication (HTTP request).

  6. Server Responds (Receiving the Tale): Your pen pal receives the request, finds the story, and sends it back (HTTP response).

  7. Rendering the Page (Enjoying the Story): Your imagination (browser) unpacks the story, assembles the sentences, and presents the complete tale on your cozy reading spot (screen).

More info

If you're interested in a deeper understanding of these topics, check out these tutorial videos:

🔸New component

ESP32

The ESP32 is widely favored for various IoT applications, offering both Wi-Fi and Bluetooth connectivity. It simplifies complex functionalities like Wi-Fi, TCP/IP, Bluetooth, and HTTP by providing predefined AT commands. In simple terms, AT commands are a set of instructions for devices to communicate, often using serial communication.

In our setup, the ESP32 acts as a Wi-Fi module connected via a UART port. To communicate with it, the SwiftIO Micro simply sends these predefined AT commands.

For an even simpler experience, we've crafted the ESP32ATClient driver. This lets you operate the module effortlessly by using straightforward APIs, eliminating the need to directly handle those commands.

The ESP32 microcontroller supports three main Wi-Fi modes:

  • Station (STA): The ESP32 connects to an existing Wi-Fi network (like a router or hotspot) as a client. In this tutorial, we'll dive into station mode.
  • Soft Access Point (SoftAP): the ESP32 becomes a Wi-Fi access point (AP), providing its own Wi-Fi network. Other devices can connect to it, similar to connecting to a router.
  • Soft Access Point + Station Mode (STA+AP): this mode combines both Station and SoftAP modes.