Skip to content

13. Networking and Communications: Richard Shan & Connor Cruz

For this week, our assignment was to send a message between two projects. We used what we did for our individual assignment for this.

General Idea

Richard’s individual project entailed controlling an ESP32 with laptop via a wireless connection. Connor’s individual project included a wired I2C connection from a master Xiao RP2040 to slave devices, constisting mainly of ATTiny412 chips. Our basic idea to communicate between these chips was for the Xiao RP2040 to have a wired connection to the ESP32. From the computer, a wireless signal would be sent to the ESP32 via a local network, which would provide power to the RP2040. The RP2040 would then send a message to the slave ATTiny412 to turn on an LED. Essentially, a wireless message would control the LED.

Configuring ESP32 for Communication

The ESP32 will receive commands through WebSocket, which establishes a wireless HTTP connection on a local Wi-Fi network.

WebSocket connections are initiated through HTTP protocol, using an upgrade request from HTTP to WebSocket. This begins with a client sending a standard HTTP request that includes an “Upgrade: websocket” header and a “Connection: Upgrade” header to the server. The server then responds with an HTTP 101 status code, indicating that the protocol will change, thus establishing the WebSocket connection.

WebSocket, uses IP addresses to facilitate the initial connection before upgrading to the WebSocket protocol. Once the WebSocket connection is established, the IP addresses are used to maintain the connection over which data frames can be reliably transmitted back and forth.

Once the WebSocket connection is established, data is transmitted in framed messages through backend data transmission ports, where each frame consists of an opcode to indicate the type of data being transmitted (e.g., text, binary, continuation frame, or control frames like close, ping, or pong). This structure allows the WebSocket protocol to be extremely versatile and efficient in handling different types of data seamlessly. The frames are small and allow for very efficient data transmission.

I have documented the libraries and setup for the ESP32CAM here.

This is the main code on the ESP32CAM that receives and acts upon the wireless commands of powering on and off the RP2040.

#include <WiFi.h>
#include <WebSocketsServer.h>
#include "Arduino.h"

#define POWER_PIN 2

const char* ssid = "REDACTED";
const char* password = "REDACTED";

WebSocketsServer webSocket = WebSocketsServer(81);

void onWebSocketEvent(uint8_t client_num, WStype_t type, uint8_t *payload, size_t length);

void setup() {
  pinMode(POWER_PIN, OUTPUT);
  Serial.begin(9600);
  while (!Serial); // Wait for the serial connection to initialize
  Serial.setDebugOutput(true);
  Serial.println();

  WiFi.begin(ssid, password);
  WiFi.setSleep(false);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  webSocket.begin();
  webSocket.onEvent(onWebSocketEvent);
  // startCameraServer();

  Serial.print("Connection Ready! Use 'http://");
  Serial.print(WiFi.localIP());
  Serial.println("' to connect");
}

void loop() {
  webSocket.loop();
}

void onWebSocketEvent(uint8_t client_num, WStype_t type, uint8_t *payload, size_t length) {
  switch (type) {
    case WStype_DISCONNECTED:
      Serial.printf("[%u] Disconnected!\n", client_num);
      break;
    case WStype_CONNECTED:
      {
        IPAddress ip = webSocket.remoteIP(client_num);
        Serial.printf("[%u] Connection from ", client_num);
        Serial.println(ip.toString());
      }
      break;
    case WStype_TEXT:
      if (strcmp((char *)payload, "on") == 0) {
        Serial.println("on received");
        digitalWrite(POWER_PIN, HIGH);      
      }
      else if (strcmp((char *)payload, "off") == 0) {
        Serial.println("off received");
        digitalWrite(POWER_PIN, LOW);
      }
      break;
  }
}

This code initiates a wireless connection where it waits for a command of “on” or “off”. Upon receiving the command of “on”, it will log it to the console and send out power on pin 2. Upon receiving off, it stops sending output on pin 2. Pin 2 is connected to a transistor and controls the power for the RP2040 and LED.

Sending Commands

Because the ESP32CAM can receive commands wirelessly, I am able to run a python script on my computer to toggle the power of the system. The following Python code is executed on my computer to turn the LED on.

import websocket
import time

def on_error(ws, error):
    print("Error:", error)

def on_close(ws, close_status_code, close_msg):
    print("### closed ###")
    print("Close status code:", close_status_code)
    print("Close message:", close_msg)

def on_open(ws):
    def run(*args):
        time.sleep(1)
        ws.send("on")
        ws.close()
    import threading
    threading.Thread(target=run).start()

if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("ws://10.12.28.193:81",
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close)
    ws.on_open = on_open
    ws.run_forever()

Similar code is used to send the “off” message.

import websocket
import time

def on_error(ws, error):
    print("Error:", error)

def on_close(ws, close_status_code, close_msg):
    print("### closed ###")
    print("Close status code:", close_status_code)
    print("Close message:", close_msg)

def on_open(ws):
    def run(*args):
        time.sleep(1)
        ws.send("off")
        time.sleep(1)
        ws.close()
    import threading
    threading.Thread(target=run).start()

if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("ws://10.12.28.193:81",
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close)
    ws.on_open = on_open
    ws.run_forever()

These two programs each respectively send either a “on” or “off” command to the ESP32CAM. The ESP32CAM recognizes these commands as a keyword to either toggle power from pin 2 on or off.

Configuring Xiao RP2040

To make the Xiao RP2040 send a signal when the power turned on, in the master code, we simply began transmission to our desired ATTiny412 in the setup function and wrote 1 to the slave module.

#include <Wire.h>

int led = 4;

void setup() {
  Wire.begin();
  Wire.beginTransmission(8);
  Wire.write(1);
  Wire.endTransmission();
}

void loop() {
}

For the slave code, we set an event to run when that signal is received that changes a variable to 1. In the loop() function, the prorgam constantly checks if that variable is 1, and if it is, the LED is turned on.

#include <Wire.h>
int recieveVal = 0;
int led = 4;

void setup() {
  pinMode(led, OUTPUT);
  Wire.begin(8);
  Wire.onReceive(recieveEvent);
}
void loop() {
  if (d1 == 1) digitalWrite(led, HIGH);
  else digitalWrite(led, LOW);
  delay(100);
}

void recieveData() {
  while (Wire.available())
  {
    receiveVal = Wire.read();
  }
}

We uploaded both of these programs to the chips, and they seemed to work well.

NPN Transistor Board

For the setup that we built, we needed to power the RP2040/LED system with the ESP32CAM board. A simple way to do this would be outputting power through digitalWriting a GPIO pin on the ESP32. Unfortunately, while that solution technically works, it doesn’t output enough voltage to actually turn on the LED and thus there is no visible change.

To solve this problem, Richard did some research online and decided to use a NPN 2N3904 transistor that would allow the toggling of the 5V pin from the ESP32, essentially allowing us to control the output of power while keeping 5V.

We first built a breadboard system to verify it worked.

We then designed and produced a PCB.

Unfortunately, this PCB didn’t work because the legs of the NPN transistor were accidentally scrambled in the schematic. However, we were able to solve this by manually twisting the legs of the transistor into the right configuration then soldering it on. We also did this on a new PCB since the old one was getting a little messy.

This new, working PCB allows us to wire GPIO2 of the ESP32 to the transistor and use that GPIO2 pin to control the output flow of the 5V pin. This essentially allows us to digitalWrite and toggle output while maintaining 5 volts which is enough to power the RP2040 and LED.

We named the board Eugepae after the Latin exclamation, meaning “Great joy!”

Working Communication

Once we had all of the components ready, we were able to successfully communicate between the laptop and the slave ATTiny412. Here is a video (the LED that is controlled is on the board furthest away from the computer near the bottom of the screen):


Last update: May 15, 2024