Skip to content

Week11: Networking and Communications

Group Assignment

  • send a message between two projects

This group assignment document was written by Naoki Hayashi.

1. Networking using Wi-Fi and internet (FMCU)

Firstly, we tried the FMCU, a button device that shows when a Fab Lab is active by sending a signal online. In addition to lab status, we added e-mail notifications to FabLab Kannai members when the button is pushed.

FMCU documentation

Communications and networking

  • Push button signal is sent from ESP32 to local router via Wi-Fi (communications)
  • The signal is sent to MQTT server and updates FMCU status via internet (networking)
  • The signal is also sent to SMTP server and send an e-mail via internet (networking)

FCMU_diagram

MQTT: A lightweight messaging protocol for sending data between devices over the internet using a publish/subscribe model.

SMTP: A protocol used to send emails from one server to another over the internet.

Hardware setup

  • XIAO ESP32C3 for Wi-Fi connection
  • LED: connected to D7 pin
  • Resistor (1kΩ) for the LED
  • Push button: connected to D6 pin

staffing

wiring

Programming

Based on the code fla-fmcu.ino, we added code to send e-mail.

  • Open Arduino IDE and write the code below
  • Add configuration file to set lab ID, Wi-Fi SSID and password, by adding config.h file
    • Push “…” at the right of the sketch window
    • A file is created at the same folder as the Arduino sketch
  • Install libraries (Sketch > Include Library > Library manager)
    • Adafruit_MQTT
    • Adafruit_MQTT_Client
    • ESP_Mail_Client
config.h
1
2
3
4
5
// Your fablabs.io lab id
const char id[] = "Lab ID goes here";

#define WLAN_SSID "SSID goes here"    // Your wifi name
#define WLAN_PASS "password goes here"    // Your wifi password
Arduino code
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
////////////////////////////////////////////////////////////////////////////////
// FMCU Button code
// By Fran Sanchez (The Beach Lab)
// January 2022
//
// IDE:
//   Arduino 2.0.0
// Platform:
//   esp32 2.0.5 - https://github.com/espressif/arduino-esp32
// Board:
//   XIAO_ESP32C3
// Libraries:
//   Adafruit_MQTT
////////////////////////////////////////////////////////////////////////////////

// Include necessary libraries
#include <WiFi.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include "config.h"
#include <ESP_Mail_Client.h>

// SMTP server settings (for sending emails)
#define SMTP_HOST "smtp.gmail.com"
#define SMTP_PORT 587

// Email login credentials
#define AUTHOR_EMAIL "email address goes here"
#define AUTHOR_PASSWORD "password goes here"

// Email recipients
#define RECIPIENT_EMAIL "email address goes here"

// Declare SMTP session and message objects
SMTPSession smtp;
Session_Config config;
SMTP_Message message;

////////////////////////////////////////////////////////////////////////////////

// Set pin numbers (based on FabXAIO board)
const int buttonPin = D7;   // Pushbutton connected to GPIO 10
const int ledPin    = D6;   // LED connected to internal GPIO

////////////////////////////////////////////////////////////////////////////////

// Variable to store button state
int buttonState = 0;

// MQTT settings
#define AIO_SERVER      "fmcu.fablabs.io"
#define AIO_SERVERPORT  1883   // Non-SSL port
#define AIO_USERNAME    "fmcu"
#define AIO_KEY         "Push 1 Button"

// Create a WiFi client for MQTT
WiFiClient client;

// Initialize MQTT client with server info
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

// Define a feed to publish to
Adafruit_MQTT_Publish alive = Adafruit_MQTT_Publish(&mqtt, "fmcu/id");

void setup()
{
  // Start serial communication
  Serial.begin(9600);
  delay(100);

  // Set pin modes
  pinMode(buttonPin, INPUT_PULLDOWN);
  pinMode(ledPin, OUTPUT);

  // Connect to WiFi
  Serial.println(); Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);
  delay(1000);

  WiFi.begin(WLAN_SSID, WLAN_PASS);
  delay(2000);

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

  Serial.println("WiFi connected");
  Serial.println("IP address: "); Serial.println(WiFi.localIP());

  delay(300);

  // Set SMTP server and authentication info
  config.server.host_name = SMTP_HOST;
  config.server.port = SMTP_PORT;
  config.login.email = AUTHOR_EMAIL;
  config.login.password = AUTHOR_PASSWORD;

  // Set time server and timezone info
  config.time.ntp_server = "pool.ntp.org,time.nist.gov";
  config.time.gmt_offset = 3;
  config.time.day_light_offset = 0;

  // Compose the email message
  message.sender.name = "TEST Mail";
  message.sender.email = AUTHOR_EMAIL;
  message.subject = "Test sending Email";
  message.addRecipient("Recipient name goes here", RECIPIENT_EMAIL);
  message.text.content = "Hello! This is a test email sent from ESP32.";

  // Enable debug output
  smtp.debug(1);

  // Set callback function for email status
  smtp.callback(smtpCallback);

  // Connect to SMTP server
  smtp.connect(&config);
}

// Callback to show email sending status
void smtpCallback(SMTP_Status status)
{
  Serial.println(status.info());

  if (status.success())
  {
    Serial.println("Email has been sent!");
  }
}

// Function to blink LED quickly (used for status)
void blinkLED(){
  for(int i = 0; i < 10; i++){
    digitalWrite(ledPin, HIGH);
    delay(50);
    digitalWrite(ledPin, LOW);
    delay(50);
  }
}

void loop()
{
  // Check WiFi connection
  if (WiFi.status() != WL_CONNECTED){
    Serial.println("WiFi not connected....");
    blinkLED();  // Blink LED if not connected
  } else {
    // Connect or reconnect to MQTT server
    MQTT_connect();

    // Read button state
    buttonState = digitalRead(buttonPin);

    // If button is pressed
    if (buttonState == HIGH) {
      // Publish ID to MQTT feed
      Serial.print(F("\nSending id "));
      Serial.print(id);
      Serial.print(F(" to fmcu feed..."));
      if (! alive.publish(id)) {
        Serial.println(F("Failed"));
      } else {
        Serial.println(F("OK!"));
        digitalWrite(ledPin, HIGH);  // Turn on LED

        // Send email
        if (!MailClient.sendMail(&smtp, &message)) {
          Serial.println("Error sending Email, " + smtp.errorReason());
        }

        delay(500);  // Delay before next loop
      }
    } else {
      digitalWrite(ledPin, LOW);  // Turn off LED if button not pressed
    }
  }

  delay(100);  // Small delay before next loop
}

// Function to manage MQTT connection
void MQTT_connect() {
  int8_t ret;

  // Return if already connected
  if (mqtt.connected()) {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  uint8_t retries = 3;

  // Try to connect to MQTT
  while ((ret = mqtt.connect()) != 0) {
    Serial.println(mqtt.connectErrorString(ret));
    Serial.println("Retrying MQTT connection in 5 seconds...");
    mqtt.disconnect();
    blinkLED();
    delay(5000);
    retries--;

    if (retries == 0) {
      // If retries fail, enter infinite loop
      while (1);
    }
  }

  Serial.println("MQTT Connected!");
}

FMCU site (at the time, only Fab Lab Kannai and Skylab Workshop were active)

Button pushed and LED turned on, FMCU site updated, Fab Lab Kannai became active (behind SkyLab) and e-mail sent

E-mail “Hello!” email


2. I2C communication (Grove Vision AI)

Secondly, we tried to send the object detection results from the Grove Vision AI Module to the ESP32 via I2C. We then explored serial communication and OLED display, based on Examples for Grove Vision AI V2 and XIAO ESP32.

In this example, we use a camera attached on a servo motor to detect a face, measure its position within the field of view, and rotate a servo motor to follow it.

Hardware setup

Use 360° continuous rotation servo

Initially, we used standard servo motor which have a limited range of motion (typically around 180 degrees), and it didn’t work as we expected (continuously switched direction).

Communications in this setup

  • Grove Vision AI Module send recognition results to XIAO ESP32 via I2C
  • XIAO ESP32 sends text to OLED via I2C
  • XIAO ESP32 sends text to PC (serial print) via USB

In this setup, both the OLED display and the Grove Vision AI V2 module connect to the XIAO ESP32 using the I2C protocol, which uses only two wires: SDA (data) and SCL (clock). Each device has a unique address, so the ESP32 can talk to them one at a time over the same wires. In this case, for OLED display, the I2C communication is handled internally by the U8x8lib library once u8x8.begin() is called.

hardware_AI_vision

Steps

  1. Load preset AI model to Grove Vision AI Module
    • Visit Seeed Studio SenseCraft
    • Connect Grove Vision AI Module to PC using USB
    • Select AI models or upload custom model (in this case, we used “Face Detection”) SenseCraft
    • Disconnect from PC
  2. Wiring
    • Connect XIAO to the expansion board
    • Wire Grove Vision module to expansion board via I2C connector
    • Wire the servo motor to the expansion board Connect the servo motor’s control wire to the D6 pin on the XIAO ESP32. Power the servo using the 5V output from the XIAO ESP32.
  3. Program XIAO ESP32

    • Select board (If it is not downloaded, Tools > Board > Boards Manager > Search “ESP32”)
    • Install libraries: U8x8 for the OLED display (Sketch > Include Library > Library manager > Search “U8x8” and install it)
    • After uploading program, Arduino IDE says “Hard resetting with RTC WDT…” Then push the RESET button on XIAO ESP32.

    Arduino code

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    #include <Arduino.h>
    #include <U8x8lib.h> // Include the U8x8 library for OLED display
    
    // Initialize the OLED display using I2C communication (128x64 resolution)
    U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);
    
    #include <Seeed_Arduino_SSCMA.h> // Include the library for the vision inference module
    SSCMA Infer; // Create an instance of the inference module
    
    // Function to stop the servo by sending a neutral pulse
    void stop_rotate(void)
    {
        digitalWrite(D6, HIGH);
        delayMicroseconds(1500); // Neutral pulse for stopping
        digitalWrite(D6, LOW);
    }
    
    // Function to rotate the servo
    // rtime: how long to rotate, revers: direction of rotation
    void servo_rotate(int rtime, bool revers = false)
    {
        uint32_t sleep;
        if (revers)
        {
            sleep = 2500; // Pulse for reverse direction
        }
        else
        {
            sleep = 500; // Pulse for forward direction
        }
    
        digitalWrite(D6, HIGH);
        delayMicroseconds(sleep); // Send pulse
        digitalWrite(D6, LOW);
        delay(30 * rtime); // Wait for motion
        stop_rotate(); // Stop the servo after motion
    }
    
    void setup()
    {
        Infer.begin(); // Start the inference module
        Serial.begin(9600); // Start serial communication
        pinMode(D6, OUTPUT); // Set pin D6 as output (connected to servo)
    
        u8x8.begin(); // Initialize the OLED display
        u8x8.setFlipMode(1); // Flip the display (180 degrees)
    }
    
    void loop()
    {
        // Check if inference is ready
        if (!Infer.invoke())
        {
            // If at least one object is detected
            if (Infer.boxes().size() > 0)
            {
                // If object is on the left side, rotate servo in reverse
                if (Infer.boxes()[0].x < 160)
                {
                    servo_rotate(1, true);
                    delay(50);
                }
                // If object is on the right side, rotate servo forward
                else if (Infer.boxes()[0].x > 240)
                {
                    servo_rotate(1, false);
                    delay(50);
                }
    
                // Print detected coordinates to the serial monitor
                Serial.print("X==>>");
                Serial.print(Infer.boxes()[0].x);
                Serial.print("Y==>>");
                Serial.println(Infer.boxes()[0].y);
    
                // Display coordinates on OLED screen
                u8x8.setFont(u8x8_font_chroma48medium8_r); // Set font
                u8x8.setCursor(0, 0); // Set cursor to top-left
                u8x8.print("X="); // Print X coordinate
                u8x8.println(Infer.boxes()[0].x);
                u8x8.print("Y="); // Print Y coordinate
                u8x8.println(Infer.boxes()[0].y);
            }
        }
    }
    

Detects and tracks Shintaro-san’s face

OLED displaying X and Y position of detected face

Week 11 Group Work Completed. And on to the Individual Assignment…

Each person’s URL is listed