Week 13: Networking and Communications

April 28, 2024

Networking and Communications

This week, I focused on setting up and testing the network system that will be used in my ToGo project. The primary goal was to establish a robust communication network between multiple ESP32-C3 microcontrollers and a Raspberry Pi 4, which acts as the central controller. This setup is crucial for ensuring responsiveness and reliability in signal handling as the ESP32 devices receive and execute commands based on numbers detected from Aruco codes through a connected camera.

Alt Text

Group Assigment Page

group assignment networking link 🚀

Project Objectives and Goals

Network Setup and Experiments

I experimented with an architecture consisting of 4 ESP32-C3 devices and 1 Raspberry Pi, all communicating within the same network using Flask to facilitate connections. This setup allowed for the following functionalities:

Alt Text

Project Architecture Overview

This section provides an overview of the network architecture and communication flow within the project:

Communication Flow

[Raspberry Pi] –HTTP/UDP–> [ESP32 Devices]

Master Controller: Raspberry Pi

Alt Text Camera Conncetion

Camera Setting Alt Text Alt Text

Post - Send Packet Alt Text

Aruco Detect Alt Text Alt Text Alt Text Alt Text

Code Section

Flask(app.py)

from flask import Flask, render_template, jsonify
import threading
import requests
import time
import cv2
from cv2 import aruco
import numpy as np
#import ledstrip
from picamera2 import Picamera2

app = Flask(__name__)

# Global variabels
picam2 = None

def setup_camera():
    global picam2
    picam2 = Picamera2()
    camera_config = picam2.create_still_configuration(main={"size": (1920, 1080)}, lores={"size": (640, 480)}, display="lores")
    picam2.configure(camera_config)
    print("[INFO] Camera is configured.")

def take_photo_and_detect_aruco():
    global picam2
    print("[INFO] Taking picture...")
    picam2.start()
    frame = picam2.capture_array()
    picam2.stop()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    aruco_dict = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)
    parameters = aruco.DetectorParameters_create()
    corners, ids, rejectedImgPoints = aruco.detectMarkers(gray, aruco_dict, parameters=parameters)
    
    if ids is not None:
         # ArUco algılandığında LED'leri yeşil yap
        #ledstrip.flash_green(10)
        print(f"Detected ArUco IDs: {ids.flatten()}")
        control_led_by_id(ids.flatten().tolist())
        send_aruco_ids_to_esp(ids.flatten().tolist())
        return ids.flatten().tolist()
    else:
        print("No ArUco markers detected")
        return []
def send_aruco_ids_to_esp(ids):
    ids_str = ','.join(map(str, ids))
    try:
        response = requests.get(f"http://192.168.68.120/show_aruco?ids={ids_str}")
        print("ArUco IDs sent to ESP")
    except Exception as e:
        print(f"Error sending ArUco IDs to ESP: {e}")

def control_led_by_id(ids):
    esp_control = {
        "192.168.68.120": {"range": range(1, 7), "on": "http://192.168.68.120/D10/on", "off": "http://192.168.68.120/D10/off"},
        "192.168.68.121": {"range": range(7, 13), "on": "http://192.168.68.121/D10/on", "off": "http://192.168.68.121/D10/off"},
    }
    for esp_ip, info in esp_control.items():
        if any(id_ in info["range"] for id_ in ids):
            try:
                # LED'i aç
                requests.get(info["on"], timeout=2)
                print(f"LED turned on at ESP {esp_ip} for IDs {ids}")
                # 10 saniye sonra LED'i kapat
                threading.Timer(10, lambda esp_ip=esp_ip: requests.get(esp_control[esp_ip]["off"], timeout=2)).start()
            except Exception as e:
                print(f"Error controlling LED at ESP {esp_ip}: {e}")

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/connect_camera', methods=['GET'])
def connect_camera():
    threading.Thread(target=setup_camera).start()
    return jsonify({'message': 'Camera connection is initiated.'})
    #threading.Thread(target=lambda: (setup_camera(), ledstrip.flash_yellow(5))).start()
    #return jsonify({'message': 'Camera connection is initiated and LEDs flashed yellow for 5 seconds.'})


@app.route('/take_photo', methods=['GET'])
def photo_route():
    ids = take_photo_and_detect_aruco()
    if ids:
        return jsonify({'detected_ids': ids})
    else:
        return jsonify({'message': 'No ArUco markers detected'})

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

Cube Code

#include <WiFi.h>
#include <U8g2lib.h>
#include <Adafruit_NeoPixel.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

#define PIN D9 // Pin connected to the RGB LED
#define NUMPIXELS 1 // Number of LEDs
#define NORMAL_LED D10 // Pin connected to the normal LED
bool ledState = false; // LED initially off

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, SCL, SDA, U8X8_PIN_NONE);

const char* ssid = "*********";
const char* password = "**********";
WiFiServer server(80);

IPAddress local_IP(192, 168, 68, 122);
IPAddress gateway(192, 168, 68, 1);
IPAddress subnet(255, 255, 255, 0);

String header;
String arucoIDs = "";

void setup() {
  Serial.begin(115200);
  pixels.begin();
  pixels.show(); // Turn off all LEDs initially
  u8g2.begin();
  u8g2.enableUTF8Print();

  pinMode(NORMAL_LED, OUTPUT); // Set the normal LED pin as output
  digitalWrite(NORMAL_LED, LOW); // LED initially off
  
  if (!WiFi.config(local_IP, gateway, subnet)) {
    Serial.println("STA Failed to configure");
  }
  
  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());
  server.begin();
}

void loop() {
  WiFiClient client = server.available();
  if (client) {
    Serial.println("New Client.");
    String currentLine = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        header += c;
        if (c == '\n') {
          if (currentLine.length() == 0) {
            // Receive ArUco IDs and control LEDs
            if (header.indexOf("GET /show_aruco?ids=") >= 0) {
              int startPos = header.indexOf('=') + 1;
              int endPos = header.indexOf(' ', startPos);
              arucoIDs = header.substring(startPos, endPos);
              Serial.println("Received ArUco IDs: " + arucoIDs);
              digitalWrite(NORMAL_LED, HIGH); // Turn on the normal LED
              // You can set the RGB LED to a specific color here
              flashDifferentColors(); // Flash in different colors
              pixels.clear(); // Turn off the LED
              pixels.show();
              
            }
            if (header.indexOf("GET /D10/on") >= 0) {
              Serial.println("Normal LED on");
              digitalWrite(NORMAL_LED, HIGH); // Turn on the normal LED
              ledState = true; // Update LED state
            } else if (header.indexOf("GET /D10/off") >= 0) {
              Serial.println("Normal LED off");
              digitalWrite(NORMAL_LED, LOW); // Turn off the normal LED
              ledState = false; // Update LED state
            }
            // Web server response
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("</head><body>");
            client.println("<h1>ESP32 Web Server</h1>");
            client.println("</body></html>");
            client.stop();
          } else {
            currentLine = "";
          }
        } else if (c != '\r') {
          currentLine += c;
        }
      }
    }
    header = "";
    Serial.println("Client Disconnected.");
  }

  // OLED screen updates and other processes...
  // Print ArUco IDs and other information on the OLED
  updateOledDisplay();
}

void updateOledDisplay() {
  u8g2.clearBuffer(); // Clear the screen content
  u8g2.setFont(u8g2_font_6x10_tf); // Set font size

  // Print the WiFi MAC address
  u8g2.setCursor(0, 10); // Position the cursor
  u8g2.print("MAC: ");
  u8g2.print(WiFi.macAddress());

  // Print the WiFi IP address
  u8g2.setCursor(0, 20); // Position the cursor
  u8g2.print("IP: ");
  u8g2.print(WiFi.localIP());

  // Print ArUco IDs
  u8g2.setCursor(0, 30); // Position the cursor
  u8g2.print("ArUco IDs: ");
  u8g2.print(arucoIDs);

  // Print LED status
  u8g2.setCursor(0, 40); // Position the cursor
  u8g2.print("LED: ");
  u8g2.print(ledState ? "On" : "Off"); // Print "On" or "Off" based on LED status

  u8g2.sendBuffer(); // Display updates on the screen
}

void flashDifferentColors() {
  // Flash in different colors at specified intervals
  for(int i = 0; i < 3; i++) { // 3 cycles for each color
    pixels.setPixelColor(0, pixels.Color(255, 0, 0)); // Red
    pixels.show();
    delay(1000); // Wait 1 second
    pixels.setPixelColor(0, pixels.Color(0, 255, 0)); // Green
    pixels.show();
    delay(1000); // Wait 1 second
    pixels.setPixelColor(0, pixels.Color(0, 0, 255)); // Blue
    pixels.show();
    delay(1000); // Wait 1 second
  }
}

How It Works

Flask Application (app.py)

The Flask application operates on a Raspberry Pi and performs several key functions:

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/connect_camera', methods=['GET'])
def connect_camera():
    threading.Thread(target=setup_camera).start()
    return jsonify({'message': 'Camera connection is initiated.'})

@app.route('/take_photo', methods=['GET'])
def photo_route():
    ids = take_photo_and_detect_aruco()
    if ids:
        return jsonify({'detected_ids': ids})
    else:
        return jsonify({'message': 'No ArUco markers detected'})

ESP32 Code

The ESP32 code serves as a web server and is responsible for the following tasks:

void loop() {
    WiFiClient client = server.available();  // Check if a client has connected
    if (client) {  // If a client is connected, read the HTTP request
        String currentLine = "";  // make a String to hold incoming data from the client
        while (client.connected()) {  // loop while the client's connected
            if (client.available()) {  // if there's bytes to read from the client,
                char c = client.read();  // read a byte, then
                header += c;
                if (c == '\n') {  // if the byte is a newline character
                    // if the current line is blank, you got two newline characters in a row.
                    // that's the end of the client HTTP request, so send a response:
                    if (currentLine.length() == 0) {
                        // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
                        // and a content-type so the client knows what's coming, then a blank line:
                        client.println("HTTP/1.1 200 OK");
                        client.println("Content-type:text/html");
                        client.println("Connection: close");
                        client.println();
                        // turns the GPIOs on and off
                        if (header.indexOf("GET /D10/on") >= 0) {
                            digitalWrite(D10, HIGH);  // Turn the LED on
                        } else if (header.indexOf("GET /D10/off") >= 0) {
                            digitalWrite(D10, LOW);  // Turn the LED off
                        }
                        // Break out of the while loop
                        break;
                    } else {  // if you got a newline, then clear currentLine
                        currentLine = "";
                    }
                } else if (c != '\r') {  // if you got anything else but a carriage return character,
                    currentLine += c;  // add it to the end of the currentLine
                }
            }
        }
        // Clear the header variable
        header = "";
        // Close the connection
        client.stop();
        Serial.println("Client disconnected.");
    }
}

Source Files

Source Folder