← Back to Main Workshop

Part 1: Fundamentals - Networking & Communication

In this short guide, we'll go through a few examples of how to use a wireless WiFi network and how to communicate over it.

It's important to note that there are several different libraries available for the ESP8266 module — for example, for running an HTTP server, or for TCP/IP and UDP communication — and these libraries differ slightly from one another.

Some examples found online therefore require additional libraries that need to be installed separately.

Likewise, using a server may require installing entirely new functionalities, such as a file system (SPIFFS). These topics are beyond the scope of this basic guide.

WiFi Network

WiFi Network Basics 📋 Show

There are a few key points to consider when working with wireless networks.

Connecting to an existing network (Station mode / STA mode)

– for example, your home WiFi, which is already connected to the Internet. In this case, the access point is your home router.

To join an existing network, you naturally need the network name (SSID) and the password.

If you connect to a network that has Internet access, you can send and receive messages between devices anywhere in the world.

Multiple connection points

– you can define several networks with their passwords, and the device will automatically connect to the strongest available network.

📋 Example Code: Multiple WiFi Networks (Auto-Connect)
#include <ESP8266WiFi.h>        // WiFi library for ESP8266
#include <ESP8266WiFiMulti.h>   // Enables connecting to multiple WiFi networks

ESP8266WiFiMulti wifiMulti;     // Create an ESP8266WiFiMulti object named 'wifiMulti'

void setup() {
  Serial.begin(115200);         // Initialize serial communication at 115200 baud
  delay(10);
  Serial.println('\n');

  // Add multiple WiFi networks (SSID and password)
  wifiMulti.addAP("ssid_AP_1", "your_password_AP_1");   
  wifiMulti.addAP("ssid_AP_2", "your_password_AP_2");
  wifiMulti.addAP("ssid_AP_3", "your_password_AP_3");

  Serial.println("Connecting ...");
  int i = 0;
  while (wifiMulti.run() != WL_CONNECTED) {  // Try connecting to the strongest available network
    delay(1000);
    Serial.print('.');
  }

  Serial.println('\n');
  Serial.print("Connected to ");
  Serial.println(WiFi.SSID());               // Print the SSID of the connected network
  Serial.print("IP address:\t");
  Serial.println(WiFi.localIP());            // Print the local IP address of the device
}

void loop() { 
  // Nothing to do here
}
Access Point Mode - Introduction 📋 Show

You can connect to an existing network (STA) or create your own network (AP)

If you want to use MQTT and send data anywhere on the internet, you naturally need to connect to an existing network.

Access Point Mode

If you want to communicate directly with the ESP8266, you can set the device itself to Access Point (AP) mode.

In this mode, you connect directly to the device, but you are no longer connected to the Internet.

Exercise 1: Access Point Mode (AP Mode) 📋 Show
Exercise 1: Access Point Mode (AP Mode)
#include <ESP8266WiFi.h>        // WiFi library

const char *ssid = "ESP8266 Access Point"; // Network name (SSID), not the device name
const char *password = "";                  // No password needed for testing

void setup() {
  Serial.begin(115200);
  delay(10);
  Serial.println('\n');

  WiFi.softAP(ssid, password);             // Start the device in Access Point mode
  Serial.print("Access Point \"");
  Serial.print(ssid);
  Serial.println("\" started");

  Serial.print("IP address:\t");
  Serial.println(WiFi.softAPIP());         // Print the device's IP address
}

void loop() { }
Exercise 2: WiFi Connection (STA Mode) 📋 Show
Exercise 2: WiFi Connection (STA Mode)
#include <ESP8266WiFi.h>        // WiFi library for the ESP8266 module

const char* ssid     = "SSID";         // Network name (SSID) of the access point
const char* password = "PASSWORD";     // Network password (use "" if open network)

void setup() {
  Serial.begin(115200);                // Start the serial monitor at 115200 baud
  delay(10);
  Serial.println('\n');

  WiFi.begin(ssid, password);          // Start connecting to the WiFi network
  Serial.print("Connecting to ");
  Serial.print(ssid); 
  Serial.println(" ...");

  int i = 0;
  while (WiFi.status() != WL_CONNECTED) { // Wait until the device is connected
    delay(1000);
    Serial.print(++i); 
    Serial.print(' ');
  }

  Serial.println('\n');
  Serial.println("Connection established!");  
  Serial.print("IP address:\t");
  Serial.println(WiFi.localIP());     // Print the device's local IP address
}

void loop() { 
  // Nothing to do here
}
Exercise 3: Testing the WiFi Connection 📋 Show

Connect your computer to the WiFi network: ESP8266 Access Point

On Windows, open Command Prompt.
On Mac, open Terminal.

Type the command:

đŸ’ģ Command: Test Connection
ping 192.168.4.1

HTTP Server

HTTP Server - Introduction 📋 Show

A common way to use the ESP8266 is to turn it into a web server.

This allows you to communicate with the device through a web browser over WiFi.

In practice, communication is usually based on two main HTTP commands: GET and POST.

The GET command requests information from the server — typically an HTML page or data from the ESP8266.

The POST command sends information to the server — for example, data entered in a web form or sensor readings.

With these two commands, you can already build simple interactive applications, such as controlling LEDs, sending sensor data, or updating web content.

For a practical example, see:
🔗 ESP8266 and the Arduino IDE Part 2 – Control an LED from a web page using Access Point mode (AP)

Exercise 4: LED Control Web Server 📋 Show

This example creates a web server that allows you to control an LED through a web browser.

📋 Example Code: LED Control Web Server
/*
 * Intended to be run on an ESP8266 d1 clone
 * use web browser to go to 192.168.4.1
 */
#include <ESP8266WiFi.h>

const char AP_NameChar[] = "LEDControl" ;    // Access Point name
const char WiFiPassword[] = "";              // No password for testing
WiFiServer server(80);                       // Create server on port 80

// HTTP response header
String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";

// HTML page parts
String html_1 = "<!DOCTYPE html><html><head><title>LED Control</title></head><body><div id='main'><h2>LED Control</h2>";
String html_2 = "<form id='F1' action='LEDON'><input class='button' type='submit' value='LED ON' ></form><br>";
String html_3 = "<form id='F2' action='LEDOFF'><input class='button' type='submit' value='LED OFF' ></form><br>";
String html_4 = "</div></body></html>";

String request = "";                         // Store HTTP request
int LED_Pin = 2;                            // LED connected to GPIO 2

void setup() {
  Serial.begin(115200);                     // Start serial communication
  pinMode(LED_Pin, OUTPUT);                 // Set LED pin as output
  
  // Create Access Point
  boolean conn = WiFi.softAP(AP_NameChar, WiFiPassword);
  server.begin();                           // Start the web server
}

void loop() {
  // Check if a client has connected
  WiFiClient client = server.available();
  if (!client) {
    return;
  }
  
  // Read the first line of the request
  request = client.readStringUntil('\r');
  Serial.println(request);
  
  // Control LED based on request
  if ( request.indexOf("LEDON") > 0 ) {
    digitalWrite(LED_Pin, LOW);             // Turn LED ON (LOW = ON for built-in LED)
  } else if ( request.indexOf("LEDOFF") > 0 ) {
    digitalWrite(LED_Pin, HIGH);            // Turn LED OFF (HIGH = OFF for built-in LED)
  }
  
  // Send HTTP response
  client.flush();
  client.print( header );
  client.print( html_1 );
  client.print( html_2 );
  client.print( html_3 );
  client.print( html_4);
  delay(5);
}
Exercise 5: Data Server with AsyncWebServer 📋 Show

This example shows how to serve dynamic data using the AsyncWebServer library.

Test URLs:
â€ĸ http://192.168.4.1/ (full page)
â€ĸ http://192.168.4.1/t (temperature data only)
â€ĸ http://192.168.4.1/h (humidity data only)

📋 Example Code: Data Server with AsyncWebServer
/*
 * IP address: 192.168.4.1
 * Test http://192.168.4.1/h
 * Test http://192.168.4.1/t
 */
#include "Arduino.h"
#include "ESP8266WiFi.h"
#include "ESPAsyncWebServer.h"

const char* ssid = "ESP8266";              // Access Point name
const char* password = "";                 // No password for testing

String request = "";                       // Store HTTP request
float i = 0;                              // Counter variable
float tem = 0.0;                          // Temperature variable
float hem = 0.0;                          // Humidity variable

AsyncWebServer server(80);                // Create async web server on port 80

// HTML template stored in PROGMEM (flash memory)
const char index_html[] PROGMEM = R"rawliteral(
ESP8266 Number Server <br>
Number tem %t% Yksikko <br>
Number hem %h% Yksikko <br>
)rawliteral";

// Function to replace placeholders with actual values
String processor(const String& var){
  Serial.println(var);
  if(var == "t"){
    return String(tem);                    // Return temperature as string
  } else if(var == "h"){
    return String(hem);                    // Return humidity as string
  }
  return String();
}

void setup(){
  Serial.begin(115200);                   // Serial communication for debugging
  Serial.println();
  Serial.println("Setting AP (Access Point)â€Ļ");
  
  // Create Access Point
  WiFi.softAP(ssid, password);
  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);
  
  // Route for main page - returns full HTML page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });
  
  // Route for temperature data only - returns just the value
  server.on("/t", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", String(tem).c_str());
  });
  
  // Route for humidity data only - returns just the value
  server.on("/h", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", String(hem).c_str());
  });
  
  server.begin();                         // Start the server
}

void loop(){
  tem = i;                                // Update temperature value
  hem = i;                                // Update humidity value
  i++;                                    // Increment counter
}
Exercise 6: Auto-Refreshing Web Page 📋 Show

This example shows how to create a web page that automatically refreshes itself every second.

Key feature: The page uses HTML meta refresh to update automatically without user interaction.

📋 Example Code: Auto-Refreshing Web Page
/*
 * Auto-refreshing web page example
 * Page refreshes every 1 second automatically
 */
#include "Arduino.h"
#include "ESP8266WiFi.h"
#include "ESPAsyncWebServer.h"

const char* ssid = "ESP8266";              // Access Point name
const char* password = "";                 // No password for testing

String request = "";                       // Store HTTP request
float i = 0;                              // Counter variable
float tem = 0.0;                          // Temperature variable
float hem = 0.0;                          // Humidity variable

AsyncWebServer server(80);                // Create async web server on port 80

// HTML template with auto-refresh meta tag
const char index_html[] PROGMEM = R"rawliteral(
   <head>
  <meta http-equiv="refresh" content="1"> 
</head>
ESP8266 Number Server<br>
Number tem<br>
%t% Yksikko<br>
)rawliteral"; // tem will be replaced automatically

// Function to replace placeholders with actual values
String processor(const String& var){
  Serial.println(var);
  if(var == "t"){
    return String(tem);                    // Return temperature as string
  }
  return String();
}

void setup(){
  Serial.begin(115200);                   // Serial communication for debugging
  Serial.println();
  Serial.println("Setting AP (Access Point)â€Ļ");
  
  // Create Access Point
  WiFi.softAP(ssid, password);
  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);
  
  // Route for main page - returns full HTML page with auto-refresh
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ 
    request->send_P(200, "text/html", index_html, processor);
  });
  
  server.begin();                         // Start server
}

void loop(){ 
  delay(1000);                            // Wait 1 second
  tem = i++;                              // Update temperature and increment counter
}

📚 More information:
🔗 https://www.martyncurrey.com/esp8266-and-the-arduino-ide-part-8-auto-update-webpage/

Telnet, TCP, and UDP - Introduction & Setup 📋 Show

An HTTP server is just one way to communicate with an ESP8266.

Often, it is more practical to use other communication methods, such as Telnet, TCP, or UDP, depending on your needs.

In this section, we will look at Telnet as a simple example.

Installing a Telnet Client

Before you start, you need a Telnet client (or equivalent software) on your computer.

Connecting to the ESP via Telnet

  1. Load the ESP code for Telnet communication onto your ESP8266.
  2. Make sure your ESP is connected to an existing WiFi network, such as your home or school network.
  3. Check the IP address in Arduino Serial Monitor - the ESP will print its IP address when it connects.
  4. Open a terminal (Command Prompt on Windows, Terminal on macOS/Linux).
  5. Type the following command, replacing the IP and port with your ESP's values:
đŸ’ģ Command: Connect via Telnet
telnet 192.168.51.46 8888

You should see something like this:

đŸ’ģ Terminal Output: Telnet Connection
➜  ~ telnet 192.168.51.46 8888
Trying 192.168.51.46...
Connected to 192.168.51.46.
Escape character is '^]'.
Exercise 7: Telnet Server Code 📋 Show

Here's the ESP8266 code that creates a Telnet server for bidirectional communication:

📋 Example Code: Telnet Server (STA Mode)
#include <ESP8266WiFi.h>  // Library for WiFi functionality on ESP8266

int port = 8888;           // TCP port number for the server
WiFiServer server(port);   // Create a TCP server object on the given port

const char *ssid = "aalto open";       // Your WiFi network name (SSID)
const char *password = ""; // Your WiFi password (empty for open network)

int count = 0;

void setup() 
{
  Serial.begin(115200);  // Start serial monitor for debugging
  Serial.println();

  WiFi.mode(WIFI_STA);   // Set ESP8266 to Station mode (connect to existing WiFi)
  WiFi.begin(ssid, password); // Connect to WiFi

  Serial.println("Connecting to WiFi...");

  // Wait until connected to WiFi
  while (WiFi.status() != WL_CONNECTED) {   
    delay(500);
    Serial.print(".");
    delay(500);
  }

  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());  // Print ESP's local IP address

  server.begin();  // Start the TCP server
  Serial.print("Open Telnet and connect to IP: ");
  Serial.print(WiFi.localIP());
  Serial.print(" on port ");
  Serial.println(port);
}

void loop() 
{
  WiFiClient client = server.available(); // Check if a client has connected

  if (client) {
    if(client.connected()) {  
      Serial.println("Client Connected");   // Print when a client connects
    }

    // While the client is connected, read and write data
    while(client.connected()){      
      
      // Read incoming data from the client and print it to Serial
      while(client.available() > 0){
        Serial.write(client.read()); 
      }

      // Read data from Serial (typed in Serial Monitor) and send to client
      while(Serial.available() > 0) {
        client.write(Serial.read());
      }
    }

    client.stop();  // Disconnect the client when done
    Serial.println("Client disconnected");    
  }
}
Exercise 8: Processing Telnet Client 📋 Show

You can also connect to the ESP8266 using Processing instead of Telnet:

📋 Example Code: Simple Processing Client
import processing.net.*; // Import the networking library

Client c;         // Client object to connect to ESP8266 TCP server
String data;      // String to hold received data

void setup() { 
  size(200, 200);     // Canvas size
  background(50);     // Dark background
  fill(200);          // Light text color
  
  // Connect to ESP8266 TCP server
  c = new Client(this, "192.168.51.46", 8888); // Replace with your ESP IP and port
  c.write("Hello from Processing!"); // Send a test message
} 

void draw() { 
  // Check if there's incoming data from the server
  if (c.available() > 0) { 
    data += c.readString(); // Read and append data
    println(data);          // Print received data
  } 
}
Exercise 9: Processing Data Parser 📋 Show

Here's a more advanced Processing example that parses coordinate data:

📋 Example Code: Processing Data Parser
import processing.net.*; // Import the networking library

Client c;         // Client object to connect to ESP8266 TCP server
String input;     // Raw input string from the server
int data[];       // Array to hold parsed integer values

void setup() 
{
  size(450, 255);     // Canvas size
  background(204);    // Set background color
  stroke(0);          // Default stroke color
  frameRate(5);       // Limit draw updates to 5 frames per second

  // Connect to ESP8266 TCP server
  c = new Client(this, "192.168.51.46", 8888); // Replace with your ESP IP and port
}

void draw() 
{
  // If mouse is pressed, draw and send coordinates
  if (mousePressed) {
    // Draw a line on the canvas from previous to current mouse position
    stroke(255); // White line
    line(pmouseX, pmouseY, mouseX, mouseY);

    // Send the coordinates to the server as a space-separated string
    // Format: "pmouseX pmouseY mouseX mouseY\n"
    c.write(pmouseX + " " + pmouseY + " " + mouseX + " " + mouseY + "\n");
  }

  // Check if there is data from the server
  if (c.available() > 0) {
    // Read the incoming string
    input = c.readString();
    println("Raw data received: " + input); // Debug: print raw data

    // Only take data up to the first newline
    input = input.substring(0, input.indexOf("\n"));

    // Split the string by spaces (or commas if you prefer CSV)
    // Convert each substring to an integer
    data = int(split(input, ' '));

    // Draw a line using the received coordinates
    stroke(0); // Black line
    line(data[0], data[1], data[2], data[3]);

    // Print values to verify correct parsing
    println("Parsed values: " + data[0] + ", " + data[1] + ", " + data[2] + ", " + data[3]);
  }
}
Exercise 10: CSV Data Server 📋 Show

Here's an example that sends random CSV data to connected clients:

📋 Example Code: CSV Data Server
#include <ESP8266WiFi.h>  // WiFi library for ESP8266

int port = 8888;           // TCP port number for the server
WiFiServer server(port);   // Create a TCP server on the given port

const char *ssid = "aalto open"; // WiFi SSID (default open network)
const char *password = "";      // Open network, no password

void setup() {
  Serial.begin(115200);  // Initialize serial monitor
  Serial.println();

  WiFi.mode(WIFI_STA);          // Set ESP to Station mode
  WiFi.begin(ssid, password);   // Connect to WiFi

  Serial.println("Connecting to WiFi...");

  // Wait until connected
  while (WiFi.status() != WL_CONNECTED) {   
    delay(500);
    Serial.print(".");
    delay(500);
  }

  Serial.print("\nConnected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.begin();  // Start TCP server
  Serial.print("Open Telnet or Processing client and connect to IP: ");
  Serial.print(WiFi.localIP());
  Serial.print(" on port ");
  Serial.println(port);
}

void loop() {
  WiFiClient client = server.available(); // Check for a connected client

  if (client) {
    if (client.connected()) {
      Serial.println("Client Connected");
    }

    // Send random CSV data every second
    while (client.connected()) {
      String postStr = String(random(300)) + "," +
                       String(random(300)) + "," +
                       String(random(300)) + "," +
                       String(random(300)) + "\n";

      client.write(postStr.c_str()); // Send to client
      delay(1000);
    }

    client.stop(); // Disconnect client
    Serial.println("Client disconnected");
  }
}
Exercise 11: CSV and JSON Data Formats 📋 Show

The most common ways to transmit data are CSV (Comma-Separated Values) and JSON.

CSV

Best used when data will be processed later in tools like Excel or similar spreadsheet applications.

Each row represents a record, and commas separate individual values.

JSON

A more advanced format.

Represents data as key-value pairs (variable: value), making it easier to work with in programming environments.

Supports nested structures, arrays, and objects, providing flexibility for complex datasets.

JSON Objects

A JSON object is a collection of key-value pairs, where each key is a string and each value can be a number, string, boolean, array, or even another object.

Examples

Simple object:

📋 JSON Example: Simple Object
myJSON = {"name": "John", "age": 31, "city": "New York"}

Object with an array:

📋 JSON Example: Object with Array
myJSON = {"data": [100, 20, 34, 120], "name": "Voltage"}

More complex object with multiple types:

📋 JSON Example: Complex Object
myJSON = {"Name": "circuits", "Data": 45, "DataSet": [3, 4, 56, 2, 2, 56], "check": true}

Notes

  • Viewing JSON: Tools like JSON Formatter & Viewer make it easy to read and debug JSON files.
  • Usage: JSON is widely used for data exchange. Both Arduino and Processing support parsing and generating JSON objects.

When to Use Which Format

  • Use JSON when your data has multiple types, nested structures, or arrays.
  • Use CSV for simpler tabular data.

Example: JSON Web Server

Here's a complete example that creates a web server that serves JSON data:

📋 Example Code: JSON Web Server
#include <ESP8266WiFi.h>        // WiFi library for ESP8266
#include <ESP8266WebServer.h>   // Web server library
#include <ArduinoJson.h>        // JSON handling library

// WiFi credentials
#define SSID "aalto open"    // WiFi network name
#define PASSWORD ""         // Open network, no password

ESP8266WebServer server(80); // Create a web server on port 80

// Function to send JSON data
void sendData() {
    // Create a static JSON document with 300 bytes of memory
    StaticJsonDocument<300> JSONData;

    // Add key-value pairs
    JSONData["key"] = "Value";                  // Example static key
    JSONData["number"] = String(random(100));  // Random number as string

    char data[300];           // Buffer to store serialized JSON
    serializeJson(JSONData, data);  // Convert JSON object to string

    // Send JSON response to client
    // Access in browser: http://<ESP_IP>/test
    server.send(200, "application/json", data);
}

void setup() {
    Serial.begin(115200);           // Initialize serial monitor
    Serial.println();

    // Connect to WiFi
    WiFi.begin(SSID, PASSWORD);
    Serial.print("Connecting to WiFi");

    // Wait until connected
    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        delay(1000);
    }

    Serial.println("\nConnected!");
    Serial.print("ESP IP address: ");
    Serial.println(WiFi.localIP()); // Print ESP IP address for browser access

    // Define HTTP route "/test" to send JSON
    server.on("/test", HTTP_GET, sendData); 

    // Start web server
    server.begin();
    Serial.println("Server started. Access /test in your browser");
}

void loop() {
    // Handle incoming HTTP requests
    server.handleClient();
}

How to test:

  1. Upload the code to your ESP8266
  2. Check the Serial Monitor for the IP address
  3. Open your browser and go to: http://[ESP_IP]/test
  4. You should see JSON data like: {"key":"Value","number":"42"}
Exercise 12: Processing HTTP Client 📋 Show

Here's how to connect to the JSON server using Processing:

📋 Example Code: Processing HTTP Client
import http.requests.*; // Import HTTP requests library for Processing

public void setup() {
  size(400, 400);

  // Create a GET request to the ESP8266 JSON server
  // Replace the IP below with your ESP8266's IP
  // Example: http://192.168.4.1/test if using aalto open
  GetRequest get = new GetRequest("http://192.168.4.1/test");

  get.send(); // Send the request and wait until completed

  // Print the raw response from the ESP
  println("response: " + get.getContent());

  // Parse the JSON content
  JSONObject response = parseJSONObject(get.getContent());

  // Extract and print the "number" value from the JSON
  println("number: " + response.getString("number"));
}

Note: You need to install the "HTTP Requests for Processing" library in Processing IDE before using this code.

MQTT Communication

MQTT Introduction & Setup 📋 Show

MQTT (Message Queuing Telemetry Transport) is a lightweight messaging protocol perfect for IoT devices.

It works on a publish/subscribe model where devices can send (publish) messages to topics and receive (subscribe) messages from topics through a central broker.

MQTT Broker: Shiftr.io

We'll use Shiftr.io as our MQTT broker. It's free and easy to use.

Public test broker credentials:

  • Broker: public.cloud.shiftr.io
  • Username: public
  • Password: public
  • WebSocket Port: 443 (for web browsers)
  • MQTT Port: 1883 (for ESP8266/Processing)
  • 📤 Web sends to: test/web
  • 📤 ESP8266 sends to: test/esp8266
  • 📤 Processing sends to: test/processing
  • đŸ“Ĩ Everyone subscribes to: test/# (wildcard - receives all)
Exercise 13: Web Browser MQTT Client 📋 Show

This web-based client connects to MQTT broker and can send/receive messages.

Library: MQTT.js loaded from CDN (no installation needed)

Note: This is a complete working example you can save as an HTML file and open in your browser.

📋 Example Code: Web Browser MQTT Client
<!DOCTYPE html>
<html>
<head>
    <title>MQTT Web Client</title>
    <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
    <style>
        body { 
            font-family: Arial, sans-serif; 
            padding: 20px; 
            background: #f0f0f0; 
            max-width: 600px;
            margin: 0 auto;
        }
        .container { 
            background: white; 
            padding: 20px; 
            border-radius: 8px; 
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        input { 
            width: 100%; 
            padding: 10px; 
            margin: 5px 0; 
            border: 1px solid #ddd; 
            border-radius: 4px; 
            box-sizing: border-box;
        }
        button { 
            padding: 10px 20px; 
            background: #4CAF50; 
            color: white; 
            border: none; 
            border-radius: 4px; 
            cursor: pointer; 
            margin: 5px 5px 5px 0;
        }
        button:hover { background: #45a049; }
        button:disabled { background: #ccc; cursor: not-allowed; }
        #disconnectBtn { background: #f44336; }
        #disconnectBtn:hover { background: #da190b; }
        #sendBtn { background: #2196F3; }
        #sendBtn:hover { background: #0b7dda; }
        .status { 
            padding: 10px; 
            margin: 10px 0; 
            border-radius: 4px; 
            font-weight: bold;
        }
        .connected { background: #d4edda; color: #155724; }
        .disconnected { background: #f8d7da; color: #721c24; }
        .log { 
            background: #1e1e1e; 
            color: #4CAF50; 
            padding: 10px; 
            margin: 10px 0; 
            border-radius: 4px; 
            font-family: monospace; 
            font-size: 12px;
            max-height: 300px; 
            overflow-y: auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>MQTT Web Client</h1>
        <div id="status" class="status disconnected">Disconnected</div>
        
        <h3>Connection</h3>
        <button id="connectBtn" onclick="connectMQTT()">Connect MQTT</button>
        <button id="disconnectBtn" onclick="disconnectMQTT()" disabled>Disconnect</button>
        
        <h3>Send Message</h3>
        <input type="text" id="field1Name" placeholder="Field 1 name" value="speed">
        <input type="text" id="field1Value" placeholder="Field 1 value" value="100">
        <input type="text" id="field2Name" placeholder="Field 2 name" value="direction">
        <input type="text" id="field2Value" placeholder="Field 2 value" value="forward">
        <button id="sendBtn" onclick="sendMessage()" disabled>Send Message</button>
        
        <h3>Message Log</h3>
        <div id="log" class="log">Click "Connect MQTT" to start...</div>
    </div>

    <script>
        let client = null;

        function connectMQTT() {
            if (client && client.connected) {
                addLog('âš ī¸ Already connected');
                return;
            }
            
            document.getElementById('status').textContent = 'Connecting...';
            document.getElementById('status').className = 'status';
            
            client = mqtt.connect('wss://public:public@public.cloud.shiftr.io');
            
            client.on('connect', () => {
                addLog('✅ Connected to MQTT broker');
                document.getElementById('status').textContent = 'Connected ✅';
                document.getElementById('status').className = 'status connected';
                document.getElementById('connectBtn').disabled = true;
                document.getElementById('disconnectBtn').disabled = false;
                document.getElementById('sendBtn').disabled = false;
                
                // Subscribe to all test messages
                client.subscribe('test/#');
                addLog('đŸ“Ĩ Subscribed to test/#');
            });
            
            client.on('message', (topic, message) => {
                const data = message.toString();
                addLog('📨 [' + topic + ']: ' + data);
            });
            
            client.on('error', (err) => {
                addLog('❌ Error: ' + err.message);
                document.getElementById('status').textContent = 'Error ❌';
                document.getElementById('status').className = 'status disconnected';
            });
            
            client.on('close', () => {
                addLog('🔌 Connection closed');
                document.getElementById('status').textContent = 'Disconnected';
                document.getElementById('status').className = 'status disconnected';
                document.getElementById('connectBtn').disabled = false;
                document.getElementById('disconnectBtn').disabled = true;
                document.getElementById('sendBtn').disabled = true;
            });
        }

        function disconnectMQTT() {
            if (client) {
                client.end();
                client = null;
                addLog('🔌 Disconnected from MQTT broker');
            }
        }

        function sendMessage() {
            if (!client || !client.connected) {
                alert('Not connected! Click "Connect MQTT" first.');
                return;
            }
            
            const name1 = document.getElementById('field1Name').value;
            const value1 = document.getElementById('field1Value').value;
            const name2 = document.getElementById('field2Name').value;
            const value2 = document.getElementById('field2Value').value;
            
            const message = {
                source: 'web',
                [name1]: value1,
                [name2]: value2,
                timestamp: Date.now()
            };
            
            const jsonMessage = JSON.stringify(message);
            client.publish('test/web', jsonMessage);
            
            addLog('📤 Sent to test/web: ' + jsonMessage);
        }
        
        function addLog(message) {
            const log = document.getElementById('log');
            const timestamp = new Date().toLocaleTimeString();
            log.innerHTML = timestamp + ' ' + message + '<br>' + log.innerHTML;
        }

        // Disconnect when page is closed
        window.addEventListener('beforeunload', () => {
            if (client && client.connected) {
                disconnectMQTT();
            }
        });
    </script>
</body>
</html>
Exercise 14: ESP8266 MQTT Client 📋 Show

Required library: Install "PubSubClient" by Nick O'Leary from Arduino Library Manager

📋 Example Code: ESP8266 MQTT Client
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// WiFi credentials
const char* ssid = "aalto open";
const char* password = "";

// MQTT Broker settings
const char* mqtt_server = "public.cloud.shiftr.io";
const int mqtt_port = 1883;
const char* mqtt_user = "public";
const char* mqtt_pass = "public";

WiFiClient espClient;
PubSubClient client(espClient);

int counter = 0;
unsigned long lastSend = 0;

void setup() {
  Serial.begin(115200);
  
  // Connect to WiFi
  Serial.print("Connecting to WiFi");
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("\nWiFi connected!");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  
  // Setup MQTT
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(messageReceived);
  
  // Connect to MQTT
  connectMQTT();
}

void loop() {
  // Maintain MQTT connection
  if (!client.connected()) {
    connectMQTT();
  }
  client.loop();
  
  // Send message every second to ESP8266 topic
  if (millis() - lastSend > 1000) {
    lastSend = millis();
    sendMessage();
  }
}

void connectMQTT() {
  while (!client.connected()) {
    Serial.print("Connecting to MQTT...");
    
    // Create unique client ID
    String clientId = "ESP8266_";
    clientId += String(random(0xffff), HEX);
    
    if (client.connect(clientId.c_str(), mqtt_user, mqtt_pass)) {
      Serial.println("Connected!");
      
      // Subscribe to all test topics
      client.subscribe("test/#");
      Serial.println("Subscribed to test/#");
      
      // Send hello message
      client.publish("test/esp8266", "{\"source\":\"esp8266\",\"status\":\"online\"}");
      
    } else {
      Serial.print("Failed, rc=");
      Serial.print(client.state());
      Serial.println(" Retrying in 5 seconds...");
      delay(5000);
    }
  }
}

void sendMessage() {
  // Create JSON message
  String msg = "{\"source\":\"esp8266\",\"counter\":" + String(counter) + ",\"millis\":" + String(millis()) + "}";
  
  // Publish to ESP8266 topic
  if (client.publish("test/esp8266", msg.c_str())) {
    Serial.print("Sent: ");
    Serial.println(msg);
    counter++;
  } else {
    Serial.println("Failed to send!");
  }
}

void messageReceived(char* topic, byte* payload, unsigned int length) {
  // Convert payload to string
  String message = "";
  for (unsigned int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  
  Serial.print("Received from ");
  Serial.print(topic);
  Serial.print(": ");
  Serial.println(message);
}
Exercise 15: Processing MQTT Client 📋 Show

Required library: Install "MQTT" library from Processing's Library Manager (Sketch → Import Library → Add Library)

📋 Example Code: Processing MQTT Client
import mqtt.*;

MQTTClient client;
int counter = 0;
int lastSend = 0;
String lastMessage = "";

void setup() {
  size(500, 400);
  textAlign(LEFT, TOP);
  
  // Connect to MQTT broker
  client = new MQTTClient(this);
  client.connect("mqtt://public:public@public.cloud.shiftr.io", "processing_" + hex(int(random(0xffff))));
  
  // Subscribe to all test topics
  client.subscribe("test/#");
  
  println("Connected to MQTT broker");
  println("Subscribed to test/#");
  
  // Send hello message
  client.publish("test/processing", "{\"source\":\"processing\",\"status\":\"online\"}");
}

void draw() {
  background(30);
  fill(255);
  
  // Display connection status
  textSize(16);
  text("MQTT Communication Demo", 20, 20);
  
  if (client.connected()) {
    fill(0, 255, 0);
    text("Status: Connected ✓", 20, 50);
  } else {
    fill(255, 0, 0);
    text("Status: Disconnected ✗", 20, 50);
  }
  
  // Display counter
  fill(255);
  textSize(14);
  text("Counter: " + counter, 20, 80);
  text("Sending to: test/processing", 20, 105);
  
  // Display last received message
  fill(100, 200, 255);
  text("Last received:", 20, 140);
  fill(255, 255, 100);
  textSize(12);
  text(lastMessage, 20, 165, width - 40, 200);
  
  // Instructions
  fill(150);
  textSize(11);
  text("Messages are sent every second", 20, height - 60);
  text("Open web page and ESP8266 to see all communicate!", 20, height - 40);
  text("Check shiftr.io/try to visualize the network", 20, height - 20);
  
  // Send message every second (60 frames at 60fps = 1 second)
  if (millis() - lastSend > 1000) {
    lastSend = millis();
    sendMessage();
  }
}

void sendMessage() {
  // Create JSON message
  String json = "{" +
    "\"source\":\"processing\"," +
    "\"counter\":" + counter + "," +
    "\"millis\":" + millis() +
  "}";
  
  // Publish to Processing topic
  client.publish("test/processing", json);
  
  println("Sent: " + json);
  counter++;
}

// Callback for received messages
void messageReceived(String topic, byte[] payload) {
  String message = new String(payload);
  
  println("Received from " + topic + ": " + message);
  
  // Update display
  lastMessage = "[" + topic + "]\n" + message;
}

void clientConnected() {
  println("Client connected to broker");
}

void connectionLost() {
  println("Connection lost");
}
Exercise 16: Testing MQTT Communication (Live Demo) ✕ Hide
💡 Testing tip: Open this page in two browser windows side by side. When you send a message from one window, you'll see it appear in the other window's log! This demonstrates real-time MQTT communication.
Live MQTT Demo:
â„šī¸ Connection Status: Disconnected
MQTT Messages Log:
Click "Connect MQTT" to start...
Exercise 17: Robot Control with Servos 📋 Show

Let's create a simple robot control exercise using two servos. This serves as a starting platform for future development work.

📋 Example Code: Robot Control with Two Servos
#include <ESP8266WiFi.h>  // WiFi library for ESP8266
#include <Servo.h>        // Servo control library

// ------------------- Pin Definitions -------------------
#define servoPin1 5   // Servo 1 (forward/backward)
#define servoPin2 14  // Servo 2 (left/right)

// ------------------- Default Speed -------------------
int speed = 90;      // Default speed (0-180)

// ------------------- WiFi Access Point -------------------
const char* ssid = "ESP8266_Car1";  // AP name
const char* password = "";           // Open network

// ------------------- Web Server -------------------
WiFiServer server(80);

// ------------------- Servo Objects -------------------
Servo servo1;
Servo servo2;

// ------------------- Setup Function -------------------
void setup() {
  Serial.begin(115200);  // Initialize Serial for debugging

  // Attach servos
  servo1.attach(servoPin1);
  servo2.attach(servoPin2);

  // Start Wi-Fi in Access Point mode
  WiFi.softAP(ssid, password);
  Serial.println("Access Point Started");
  Serial.print("IP Address: ");
  Serial.println(WiFi.softAPIP());

  // Start web server
  server.begin();
  Serial.println("Server started. Connect via browser to control the robot.");
}

// ------------------- Main Loop -------------------
void loop() {
  WiFiClient client = server.available(); // Check for incoming client

  if (client) {
    String currentLine = "";

    // Handle client requests
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c); // Debug: print received characters

        if (c == '\n') {
          // End of HTTP request header
          if (currentLine.length() == 0) {
            // Send HTML response
            client.println("HTTP/1.1 200 OK");
            client.println("Content-Type: text/html");
            client.println("Connection: close");
            client.println();

            // HTML interface
            client.println("<html>");
            client.println("<head><title>Robot Controller</title></head>");
            client.println("<body>");
            client.println("<h1>Control Your Car</h1>");

            // Movement buttons
            client.println("<form action=\"/forward\" method=\"get\"><button type=\"submit\">Forward</button></form>");
            client.println("<form action=\"/backward\" method=\"get\"><button type=\"submit\">Backward</button></form>");
            client.println("<form action=\"/left\" method=\"get\"><button type=\"submit\">Left</button></form>");
            client.println("<form action=\"/right\" method=\"get\"><button type=\"submit\">Right</button></form>");
            client.println("<form action=\"/stop\" method=\"get\"><button type=\"submit\">Stop</button></form>");

            // Speed slider
            client.println("<h3>Speed Control</h3>");
            client.println("<form action=\"/speed\" method=\"get\">");
            client.println("<input type=\"range\" name=\"speed\" min=\"0\" max=\"180\" value=\"" + String(speed) + "\" onchange=\"this.form.submit()\">");
            client.println("<p>Speed: <span id=\"speedValue\">" + String(speed) + "</span></p>");
            client.println("</form>");

            client.println("<script>");
            client.println("document.querySelector('input[type=range]').addEventListener('input', function() {");
            client.println("  document.getElementById('speedValue').textContent = this.value;");
            client.println("});");
            client.println("</script>");

            client.println("</body>");
            client.println("</html>");

            break; // Stop reading request
          } else {
            // Parse HTTP GET commands
            if (currentLine.indexOf("GET /forward") >= 0) moveForward();
            else if (currentLine.indexOf("GET /backward") >= 0) moveBackward();
            else if (currentLine.indexOf("GET /left") >= 0) turnLeft();
            else if (currentLine.indexOf("GET /right") >= 0) turnRight();
            else if (currentLine.indexOf("GET /stop") >= 0) stopMovement();
            else if (currentLine.indexOf("GET /speed") >= 0) setSpeed(currentLine);
          }
          currentLine = ""; // Clear line buffer
        } else if (c != '\r') {
          currentLine += c; // Accumulate HTTP request line
        }
      }
    }
    client.stop(); // Disconnect client
    Serial.println("Client disconnected");
  }
}

// ------------------- Movement Functions -------------------
void moveForward() {
  Serial.println("Moving Forward");
  servo1.write(90 - speed); // Forward for servo1
  servo2.write(90 + speed); // Forward for servo2
}

void moveBackward() {
  Serial.println("Moving Backward");
  servo1.write(90 + speed);
  servo2.write(90 - speed);
}

void turnLeft() {
  Serial.println("Turning Left");
  servo1.write(90);
  servo2.write(0);
}

void turnRight() {
  Serial.println("Turning Right");
  servo1.write(90);
  servo2.write(180);
}

void stopMovement() {
  Serial.println("Stopping");
  servo1.write(90); // Center position
  servo2.write(90); // Center position
}

void setSpeed(String request) {
  // Extract speed value from GET request
  int speedStart = request.indexOf("speed=") + 6;
  int speedEnd = request.indexOf(" ", speedStart);
  if (speedEnd == -1) speedEnd = request.length();
  
  String speedStr = request.substring(speedStart, speedEnd);
  speed = speedStr.toInt();
  
  Serial.println("Speed set to: " + String(speed));
}

How to Use

  1. Connect two servos to pins 5 and 14
  2. Upload the code to your ESP8266
  3. Connect to WiFi network "ESP8266_Car1"
  4. Open browser and go to the IP address shown in Serial Monitor
  5. Use the web interface to control your robot!

Features

  • Web Interface: HTML buttons for Forward, Backward, Left, Right, Stop
  • Speed Control: Slider to adjust speed from 0-180
  • Access Point Mode: Creates its own WiFi network
  • Real-time Control: Immediate response to button presses

Note about UDP

In this context, we'll skip UDP (User Datagram Protocol) – packet sending. UDP packets are sent out, but their delivery is not verified. TCP provides reliable communication which is more suitable for most ESP8266 applications.