WEEK 11

Networking and Communications

Week 11’s Assignment

Group assignment:

  • Send a message between two projects.
  • Document your work on the group work page and reflect on your individual page what you learned.

Link to group assignment

Individual assignment:

  • Design, build, and connect wired or wireless node(s) with network or bus addresses and a local input and/or output device(s).

Individual Assignment

This week I have to communicate between 2 boards. In the previous weeks input device week 9 and output device week 10 I had created 2 boards. I am trying to create wireless connection between them. I have used ESP-NOW (Espressif Network Operation Wireless) and BLE (Bluetooth Low Energy).

Refernce

Communication is the key... well, to everything I guess!? So under everything comes Printed Circuit Boards too.! Now, how do I make communication happen between these? This communication between the board , other components or devices is called a PCB interface. It is based on how they connect and communicate with other boards or devices and exchange information with each other. It could be via wires or wireless connections.

TO CREATE COMMUNICATION BETWEEN PCB BOARDS THERE ARE DIFFERENT WAYS.

These methods can be broadly divided into wired and wireless modes of communication.

Wired interface : Uses physical connections like cables, connectors, or traces on a PCB to transfer data, control signals, or power between devices. Protocols like UART, I2C, and SPI use wired communication. These methods are generally reliable and fast, but the communication distance is limited by the physical connection.
Wireless interfaces : These do not need physical connections to work. Data is transferred through radio waves, allowing devices to communicate without being directly connected. Common wireless communication methods include Bluetooth, Wi-Fi, and LoRa. The choice depends on factors such as range, speed, and power consumption.

UART - Universal Asynchronous Receiver/Transmitter

image source

UART is one of the simplest communication protocols and is commonly used for communication between microcontrollers, sensors, computers, and other electronic devices. It transmits data bit by bit using an asynchronous serial communication protocol. Since it is asynchronous, there is no separate clock line shared between devices. For communication to work properly, both devices must be configured with the same baud rate (communication speed). Common baud rates include 9600, 115200, and others.

It uses a TX (Transmit) and RX (Receive) wire interface to transfer data. The data is usually sent as 7 or 8 data bits wrapped between a start bit (0) and a stop bit (1) for synchronization. RX of one board must be connected to TX of the other board, and vice versa.

Because only two signal wires are needed, UART is easy to set up and is often used for debugging, serial monitoring, and communication between two devices.The decent distance commnunciation can happen is under 2 meters, maximum distance is 15 meters with low baud rate. This is important when building products.

Serial Peripheral Interface (SPI)

This is used for high speed communication between microcontrollers and sensors. They need multiple wires. Good for short distance communications inside the boards.

Inter-Integrated Circuit (I2C)

In this communication, 2 wires allow multiple devices communication using the same wires. Each device has its own unique address. Data is transferred in byte size.

Universal Serial Bus (USB)

The wired mouse or the wired keyboard (peripherals) we connect to our computer, the phones we can plug and play with, while it transfers data and power.

Pulse Width Modulation (PWM)

In simple terms, it is a technique to control the amount of power delivered to a device by turning the power on and off very quickly. Used in fading LEDs, speed control of motors, and heating elements.


Wireless Communications

Bluetooth

This is a short range network called a piconet. It includes the primary device and secondary devices that could directly speak to the primary device. Can cover up to 10 meters. This is why our Bluetooth earphones also say 10 meters connection. Low power consumption.

Wireless Fidelity (Wi-Fi)

Enables devices to connect to local area networks and the internet using radio waves. This moves data fast over a medium distance. Consumes more power than Bluetooth.

Radio Frequency Identification (RFID)

This uses electromagnetic fields to read and write data from the devices. Used in tracking and access control. Access cards use them.

ESP-NOW protocol

REFERNCES : espressif.com and seeedstudio.com

ESP-NOW is a wireless communication protocol developed by Espressif. It allows ESP devices to communicate directly with each other without the need for a Wi Fi router. It is fast, uses very little power, and devices can connect instantly using their MAC addresses. ESP-NOW can also work alongside Wi Fi and Bluetooth LE on supported ESP devices. It is a good choice for simple applications that need quick communication, such as smart home sensors, remote controls, and battery powered devices.


The gerber files are downloaded and used to mill the PCB


I had created two boards during the Input and Output Week. I wanted them to communicate with each other. The Input Week board has an ATtiny on it, and the Output Week board has a plug and play XIAO. To make it a system of three nodes, I am milling a breakout board with a plug and play XIAO on it.

SCHEMATIC DIAGRAM - Input Week board with ATtiny 1614 Week-9

Input Week board with ATtiny 1614 Week-9


SCHEMATIC DIAGRAM - Output Week board with a plug and play XIAO RP2040 Week-10

Output Week board with a plug and play XIAO RP2040 Week-10



In the rest of the documentation I have mentioned NODE and MASTER. Apparently in ESP_NOW we dont have the concept of MASTER and NODE. I learnt this later as input from instructor Saheen. You can read more about it in Getting Started with ESP-NOW

I created a breakout board to connect with my Week 9 input board. The Week 9 board uses an ATtiny1614 and includes an RTC, touch pad, and phototransistor. To add wireless communication, I connected a XIAO ESP32C6 to the ATtiny1614 through UART. The XIAO ESP32C6 is used to send data wirelessly using the ESP NOW protocol. This allows the Week 9 input board to communicate with my Week 10 output board.

Week 9 Input Board
ATtiny1614 + RTC + Touch Pad + Phototransistor

Breakout Board
XIAO ESP32C6 connected to the ATtiny1614 through UART

Week 10 Output Board
XIAO RP2040 + Servo Motor + Stepper Motor

ATtiny1614

Why the RP2040 Would Not Have Worked for This Project

For this project, I needed two boards to exchange data wirelessly using ESP-NOW. The RP2040 cannot support ESP-NOW because it does not have built in Wi Fi or Bluetooth hardware. Adding wireless communication would require additional modules, which would increase both the circuit complexity and the board space required.
Since I used a plug and play approach by adding a developer board through headers, I had the flexibility to switch between different XIAO developer boards whenever needed. The Seeed Studio XIAO RP2040, ESP32-C6, and ESP32-S3 all share the same form factor, pin count, and pin pitch, making them interchangeable without redesigning the board.
The ESP32-C6 already includes Wi Fi, Bluetooth, and ESP-NOW support, allowing direct wireless communication between the boards without any additional hardware. This made it a much more suitable choice for the networking and communication requirements of the project.

Seeed Studio XIAO ESP32-C6

The XIAO ESP32-C6 is a next generation wireless microcontroller built for modern IoT and smart home applications. It supports Wi Fi 6, Bluetooth 5.3, Thread, Zigbee, and Matter, making it ideal for devices that need to communicate across multiple wireless ecosystems while maintaining low power consumption. An IoT (Internet of Things) ecosystem is an interconnected network of physical devices embedded with sensors, software, and connectivity. It collects, shares, and acts on data automatically.

Seeed Studio XIAO ESP32-C6 - Pinout diagram Image source

Seeed Studio XIAO ESP32-S3

The XIAO ESP32-S3 is a powerful microcontroller designed for projects that need higher processing performance. It is well suited for AI, machine learning, image processing, multimedia applications, and complex interactive projects. Its mature ecosystem and strong community support can also help reduce development time.

Seeed Studio XIAO ESP32-S3 - Pinout diagram Image source

REFERENCES: SeeedStudio - Mozelectronics - Flywing-tech

With all this said, does the unique specialty of these boards actually matter for my board? Actually, no. I have only used them for touch input, motor control, and getting the time from the RTC. So I have used the XIAO ESP32 boards interchangeably in my project.

At first, I thought that I was using BLE when communicating between the boards, but I later realized that I was only using ESP-NOW. I understood that much later. Learnings do happen after you have completed trying to make the board work.

Also, this is a lot to learn in one week. I mean, I am learning about communication protocols and different boards while also trying to complete the assignment. So it just means: do your assignment. Learning is a spiral thing. (Yeah you could get lost in it)


Lets follow some traditions

The blink test

Open:

File → Examples → 01.Basics → Blink

Code explained


How much can a person forget in 1 week?

I heard about UART, and I plugged the breakout board into the ATtiny1614 board using the UART pins. I was trying to program the Microchip ATtiny1614 through the Seeed Studio XIAO ESP32C6. I thought that since these pins were connected, the XIAO ESP32C6 would be able to tell the ATtiny1614 what to do. It turns out that it does not work that way. It still needs the programmer I used in Week 9. So there I was, spending time on something that did not work. I had to be reminded by my instructor to use the programmer.
After that, every time I used the programmer to program the Microchip ATtiny1614. Then I connected the ATtiny1614 board to the breakout board with the Seeed Studio XIAO ESP32C6. Then I uploaded the program to the XIAO ESP32C6, followed by the code for the Seeed Studio XIAO ESP32S3 on the output week board, and communicated between these boards using ESP-NOW.

ESP-NOW : TOUCH + LED (half connection)

NOTE: Node 2 communicates to node 1 through node 3. This only completes a partial requirement for the assignment.

Interaction with ChatGPT

ChatGPT and touch+led-CHATGPT

board2, breakout board, board1
I am trying to communicate between two microcontrollers using Arduino. Board 1 which is the sender is seeed xiao esp32c6, this is connected on a breakout board connected to a PCB which has ATttiny1614, one led, touch pad and rtc.board 2:  this has xiao esp32s3, which is connected to a servo and stepper motor.use ESP-NOW protocol to communicate via Bluetooth.Guide to through the steps. Use minimal words.I need to the led to light up when I touch the touch pad in board 1. This lets me know the touchpad is activated. When this happens I need the servo motor in board 2 to do 1 full rotation (180 degree).when I double tap on the touchpad in board 1, I need the stepper motor in board 2 to turn 1 step.create Arduino codes for it. guide me step by step with simple short explanation

The MAC address of NODE 2 has to be got.


  #include <WiFi.h>

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  Serial.println(WiFi.macAddress());
}

void loop() {}

An LED is connected to PA3. I want it to be turned on when the touch pad is touched on Node 2. This uses ESP-NOW communication between Node 2 with the XIAO ESP32-C6 and the NODE 3/master with the XIAO ESP32-S3.


#include <esp_now.h>
#include <WiFi.h>

#define LED_PIN D9

typedef struct struct_message {
  bool touched;
} struct_message;

struct_message data;

void onReceive(const esp_now_recv_info *info, const uint8_t *incomingData, int len) {
  memcpy(&data, incomingData, sizeof(data));

  Serial.print("Received: ");
  Serial.println(data.touched ? "TOUCHED" : "NOT TOUCHED");

  if (data.touched) {
    digitalWrite(LED_PIN, HIGH);
  } else {
    digitalWrite(LED_PIN, LOW);
  }
}

void setup() {
  Serial.begin(115200);

  pinMode(LED_PIN, OUTPUT);

  WiFi.mode(WIFI_STA);

  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP NOW Init Failed");
    return;
  }

  esp_now_register_recv_cb(onReceive);
}

void loop() {
}

When I touch the touch pad on Node 2 with the XIAO ESP32-C6, I need it to communicate via ESP-NOW with the NODE2/master, which uses a XIAO ESP32-S3. An LED is connected to PA3 on the ATtiny Node 1 and should turn on.


#include <esp_now.h>
#include <WiFi.h>

#define TOUCH_PIN D6

int threshold = 500;

// Receiver MAC (S3)
uint8_t receiverMAC[] = {0x30, 0x30, 0xF9, 0x34, 0x60, 0x2C};

typedef struct struct_message {
  bool touched;
} struct_message;

struct_message data;

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);

  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP NOW Init Failed");
    return;
  }

  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, receiverMAC, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

  esp_now_add_peer(&peerInfo);
}

void loop() {
  // Touch sensing (your logic)
  pinMode(TOUCH_PIN, OUTPUT);
  digitalWrite(TOUCH_PIN, LOW);
  delayMicroseconds(10);

  pinMode(TOUCH_PIN, INPUT);

  int count = 0;
  while (digitalRead(TOUCH_PIN) == LOW) {
    count++;
  }

  Serial.print("Value: ");
  Serial.println(count);

  if (count < threshold) {
    Serial.println("TOUCHED");
    data.touched = true;
  } else {
    Serial.println("NOT TOUCHED");
    data.touched = false;
  }

  esp_now_send(receiverMAC, (uint8_t *)&data, sizeof(data));

  Serial.println("------------------");

  delay(500);
}

What happened in this is that NODE 2 puts the input and the output was seen in NODE 2 with MASTER communciating. We also need communciation from node 2 to node 1 this is misisng.
Communcaition interface :NODE 2 > ESP-NOW > NODE3/MASTER and Communcaition interface : MASTER > UART > NODE 1.

ESP-NOW : TOUCH + LED AND PHOTOTRANSISTOR + SERVO

NOTE :The communication couldn't be completed. The touch pad was connected to D6 on the XIAO ESP32S3, and it didn't work as D6 didn't have capacitive touch.

The assignment is to complete two way communication using the same interface, which in my case is ESP NOW. This time, I am communicating between NODE 1 and NODE 2 over ESP NOW with MASTER.
Communication path 1: MASTER > ESP NOW > NODE 2
Communication path 2: MASTER > UART > NODE 1

Ai interaction: Chatgpt main codes Error

    Arduino IDE with ESP32 board package version 3.3.7. 
ESP-NOW communication 
MASTER: XIAO ESP32C6 , NODE 1: ATTINY1614 , NODE2: XIAO ESP32S3 
NODE 1: ATTINY1614
PA1 : LED-A
* LED_PIN, HIGH);  // LED ON
* LED_PIN, LOW);   // LED OFF
PA2 : PHOTOTRANSISTOR
* lightValue 0 = dark, 1023 = bright
 *(lightValue < 200 ) DARK
 *(lightValue < 600) DIM
send to MASTER (ESP32C6)  via UART
PB2 : TX , PB3 : RX

NODE2: XIAO ESP32S3
D0 : microServo MG90S
*Standard servos move 0-180. 
D6 : TOUCHPAD-N
* Receive ESP-NOW data
* const int TOUCH_THRESHOLD = 20000;  
 if (touchValue < TOUCH_THRESHOLD) {
    Serial.print("Touch Detected! Value: ");

MASTER (ESP32C6):
UART connection to NODE1
D6 : TX
D7 : RX
D9 : LED-N
* TRUE turns LED ON 
* FALSE turns LED OFF 

CONDITIONS 
Master connected to node 1 via UART. MATER to NODE 2 via ESP-NOW
action 1 - WHEN “Touch Detected “ IN TOUCHPAD-N  on NODE 2  , LED-A becomes LED_PIN, HIGH on NODE-1.
action 2 - When  PA2 : PHOTOTRANSISTOR light value is “DARK” , D0 : microServo MG90S on NODE 2 does 180degree sweep once.
When either one of these actions are complete on “D9 : LED-N” BLINKS TWICE with delay500 miliseconds on MASTER

Important: 
* Code must compile without errors 
* Use correct ESP-NOW callback signatures for version 3.3.7 
* Do not include unnecessary code 
* Keep CODES separate 
* Keep the code simple and beginner friendly 

GENERATE CODES IN THIS ORDER
code 1 : mac address
code 2: NODE-1 ATTINY1615
code 3: MASTER ESP32-C6
code 4: NODE-2 ESP32-S3
 GENERATE 1 CODE THEN TROUBLESHOOT ADN THEN WILL ASK FOR THE ENXT CODE

  

CODE 1 : MAC ADDRESS OF NODE 2

First step is to get the MAC address of Node 2. A MAC address is stored in the ESP32's WiFi system. Because it was not enabled, the first code didn't give me the MAC address. With WiFi.mode(WIFI_STA); it turned on teh WiFi Hardware


#include <WiFi.h>

void setup() {
  Serial.begin(115200); //baud rate
  delay(2000); 

  WiFi.mode(WIFI_STA); //to turn on the WIFI hardware
  delay(100);

  Serial.print("MAC Address: "); //the serial output
  Serial.println(WiFi.macAddress()); // this is the MAC ADDRESS
}

void loop() {
}

The first time the WiFi hardware was off. The next code included the function to turn on the WiFi. The MAC address of XIAO ESP32-S3 is E0:72:A1:D7:7D:64 as shown in the serial monitor with baud rate 115200

CODE 2 : NODE 1 - ATTINY1614

/*
  NODE 1 - ATTINY1614

  PA1 -> LED-A
  PA2 -> Phototransistor

  PB2 -> TX
  PB3 -> RX
*/

const byte LED_PIN = PIN_PA1; //LED-A
const byte LIGHT_PIN = PIN_PA2; // Phototransistor

String rxBuffer = "";  //string is a variable that can store data received from UART or any
bool darkSent = false; //this is a check box, if something is sent-true, not done-false

void setup() {

  pinMode(LED_PIN, OUTPUT); // led would be turnd on if pressed
  digitalWrite(LED_PIN, LOW); 

  Serial0.begin(9600); //baud rate 

  Serial0.println("NODE1 READY"); // after upload - seral print
}

void loop() {

  int lightValue = analogRead(LIGHT_PIN); // setting Phototransistor pin value conditons

  if (lightValue < 200) { //condition if Phototransistor read value lower then 200 then

    if (!darkSent) { 

      Serial0.print("DARK "); //print value dark if vlaue less than 200
      Serial0.println(lightValue); //serial monitor pritns ligth avlue

      darkSent = true; // if value lower than 200 check the check box
    }

  } else {

    darkSent = false; //if not dont check the box
  }

  while (Serial0.available()) {

    char c = Serial0.read();

    if (c == '\n' || c == '\r') {

      if (rxBuffer == "LED_ON") { // if the rxbuffer gets "touch detected" input from node2

        digitalWrite(LED_PIN, HIGH); //turn led on

        Serial0.println("LED ON"); //serial monitor led on
      }

      else if (rxBuffer == "LED_OFF") { // if the condition not satisfied, led off
 
        digitalWrite(LED_PIN, LOW); //LED off

        Serial0.println("LED OFF"); //serial monitor reads led off
      }

      rxBuffer = ""; //if the node 2 input is received

    } else {

      rxBuffer += c; //if the node 2 input is not received
    }
  }

  delay(100);
}
  

Errors, errors, errors!! Things to keep in mind:

There was an error in uploading because Serial1. given in the first code was flagged by the program. Then it suggested to use Serial0.

This is the board setup one needs to follow. I kept on running into errors because either one was changed. First of all, I used a Porsch Paste SMART UPDI programmer. Since it's a smart UPDI programmer, "Optiboot" was not needed. I changed the board from “ATtiny1614…w/Optiboot” to “ATtiny3224/1624/1614…”. On choosing the board “ATtiny3224/1624/1614…”, the chip changed to ATtiny3224 and it gave an uploading error, so I changed that to ATtiny1614. The clock speed had to be this, and the programmer was also changed.

Here, even after the previous changes in settings, I faced an error in uploading. So ChatGPT suggested to upload by either selecting from SKETCH > UPLOAD USING PROGRAMMER or using Ctrl+Shift+U. Then the uploading worked.

CODE 3 : MAC ADDRESS - MASTER - ESP32-C6

#include  //wifi

void setup() {
  Serial.begin(115200); //baud rate

  WiFi.mode(WIFI_STA); //turn wifi harware on

  Serial.println(WiFi.macAddress()); //pritn MAC address
}

void loop() {
}

MAC adress of ESP32-C6 7C:2C:67:64:BA:F8

CODE 4 : MASTER - ESP32-C6

#include  //wifi 
#include  //esp now library

// UART to ATTINY1614
#define RX_PIN D7
#define TX_PIN D6 // pins UART

// Status LED
#define LED_N D9 //led on master

HardwareSerial Node1Serial(1); //uart conenction

// NODE2 MAC
uint8_t node2MAC[] = {0xE0, 0x72, 0xA1, 0xD7, 0x7D, 0x64}; //enter the MAC address

String rxBuffer = ""; //an empty text box that keeps incoming data from node 2 

// ESP-NOW message
typedef struct {
  char text[16]; //this is the  text message sthat the string rx buffer box can hold
} Message;

Message msg; //Creates a message object named msg.It is the actual container used to send or receive text.

// --------------------
// Blink Master LED
// --------------------
void blinkTwice() {

  for (int i = 0; i < 2; i++) {

    digitalWrite(LED_N, HIGH); //led on
    delay(500); //delay of .5seconds

    digitalWrite(LED_N, LOW); //led off
    delay(500); //delay of .5seconds
  }
}

// --------------------
// ESP-NOW Receive
// ESP32 package 3.3.7
// --------------------
void onReceive(const esp_now_recv_info_t *info, //the input received from node 1 or node 2
               const uint8_t *data,
               int len) {

  Message incoming;

  memcpy(&incoming, data, sizeof(incoming));

  if (strcmp(incoming.text, "TOUCH") == 0) {

    Node1Serial.println("LED_ON");

    blinkTwice();
  }
}

void setup() {

  Serial.begin(115200);  //baud rate

  pinMode(LED_N, OUTPUT); //led is output
  digitalWrite(LED_N, LOW); //led is off

  Node1Serial.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN);

  WiFi.mode(WIFI_STA); //wifi hardware is on

  if (esp_now_init() != ESP_OK) {  //id esp conenction is fine 
    Serial.println("ESP-NOW Init Failed");
    return;
  }

  esp_now_register_recv_cb(onReceive); //run onReceive function when message arrives in ESP-NOW

  esp_now_peer_info_t peerInfo = {}; //stores info of the other board

  memcpy(peerInfo.peer_addr, node2MAC, 6); //Stores NODE 2's address so it knows where to send messages

  peerInfo.channel = 0; //if this is the condition 
  peerInfo.encrypt = false; //no encryption 

  esp_now_add_peer(&peerInfo);

  Serial.println("MASTER READY"); //serial print 
}

void loop() {

  while (Node1Serial.available()) {

    char c = Node1Serial.read();

    if (c == '\n' || c == '\r') {

      if (rxBuffer == "DARK") { //if phototranssitor is dark

        strcpy(msg.text, "SWEEP"); // if "DARK" then 180 degree sweep

        esp_now_send(node2MAC,
                     (uint8_t *)&msg,
                     sizeof(msg));

        blinkTwice(); //to do blink function 
      }

      rxBuffer = "";

    } else {

      rxBuffer += c;
    }
  }
}

Learnt the lesson of how important it is to notice the pinout diagram and understand it. XIAO ESP32S3 has touch capability on pins D0 to D5, and D8 to D10. Guess which pin I had my touch pad connected to? D6, which does not have capacitive touch. So now I have to exchange the XIAOs.


ONE WHERE THE COMMUNICATION IS COMPLETE

ESP-NOW : TOUCH + LED AND PHOTOTRANSISTOR + SERVO

Prompt : Gemini- Draw a very simple but accurate architecture communication between boards using esp now and uart. There are three nodes. Node 1, which has an ATtiny164. Node 2, which has xiao ESP32-C6. And Node 3 also addressed as master, , with xiao ESP32-S3. Okay? There, now you have to draw the architectural communication between these boards. And if the node A, if on node A, the phototransistor sends a message dark, it is received by the master through UART connection, and that is transmitted to Node 2 via ESP-NOW connection, and the servo connected in the Node 2 sweeps 180 degrees once. And as a result, in master, the LED light blinks twice to denote the whole action is complete. And again, the next communication is from, in Node 2, if the touchpad is touched, the message touch detected is sent to master or aka Node 3. Node 3. And this is via ESP-NOW. And from the master, through UART connection, the message is sent to Node 1, saying it to blink the LED once. After this is over, the master, or the Node 3, the LED blinks twice to denote an action is complete. very easy to understand, clean

I have placed XIAO ESP32C6 on NODE 2 and XIAO ESP32S3 on MASTER. The rest is the same communicating between NODE 1 and NODE 2 over ESP NOW with MASTER.
Communication path 1: MASTER > ESP NOW > NODE 2
Communication path 2: MASTER > UART > NODE 1

Ai interaction: Chatgpt Finally codes that work

Arduino IDE with ESP32 board package 
ESP-NOW communication 
MASTER: XIAO ESP32S3 , NODE 1: ATTINY1614 , NODE2: XIAO ESP32C6 
NODE 1: ATTINY1614
PA1 : LED-A
* LED_PIN, HIGH);  // LED ON
* LED_PIN, LOW);   // LED OFF
PA2 : PHOTOTRANSISTOR
* lightValue 0 = dark, 1023 = bright
 *(lightValue < 200 ) DARK
 *(lightValue < 600) DIM
send to MASTER (ESP32S3)  via UART
PB2 : TX , PB3 : RX

NODE2: XIAO ESP32C6
D0 : microServo MG90S
*Standard servos move 0-180. 
D6 : TOUCHPAD-N
* Receive ESP-NOW data
* const int TOUCH_THRESHOLD = 20000;  
 if (touchValue < TOUCH_THRESHOLD) {
    Serial.print("Touch Detected! Value: ");

MASTER (ESP32S3):
UART connection to NODE1
D6 : TX
D7 : RX
D9 : LED-N
* TRUE turns LED ON 
* FALSE turns LED OFF 

CONDITIONS 
Master connected to node 1 via UART. MATER to NODE 2 via ESP-NOW
action 1 - WHEN “Touch Detected “ IN TOUCHPAD-N  on NODE 2  , LED-A becomes LED_PIN, HIGH on NODE-1.
action 2 - When  PA2 : PHOTOTRANSISTOR light value is “DARK” , D0 : microServo MG90S on NODE 2 does 180degree sweep once.
When either one of these actions are complete on “D9 : LED-N” BLINKS TWICE with delay500 miliseconds on MASTER

Important: 
* Code must compile without errors 
* Use correct ESP-NOW callback signatures 
* Do not include unnecessary code 
* Keep CODES separate 
* Keep the code simple and beginner friendly 
* WiFi.mode(WIFI_STA); INCLUDE THIS ISN MAC ADDRESS CODE

GENERATE CODES IN THIS ORDER
code 1 : mac address : NODE-2 ESP32-C6
code 2: NODE-1 ATTINY1615
code 3: mac address : MASTER ESP32-S3
code 4: MASTER ESP32-S3
code 5: NODE-2 ESP32-C6
 GENERATE 1 CODE THEN TROUBLESHOOT AND THEN WILL ASK FOR THE NEXT CODE

  

I asked for step by step codes and also mentioned the order.

CODE 1 : MAC ADDRESS OF NODE 2 :ESP32-C6


#include 

void setup() {
  Serial.begin(115200); //baud rate
  delay(1000); //delay 1 second 

  WiFi.mode(WIFI_STA); //turn on wifi hardware

  Serial.println();
  Serial.print("ESP32-C6 MAC Address: "); //print ESP32-C6 MAC Address:
  Serial.println(WiFi.macAddress());  //the mac address
}

void loop() { //no loop added
}
  

I added to include WiFi.mode(WIFI_STA); in the code so that there will be no issues like last time.

NODE 2 : ESP32-C6MAC ADDRESS : 7C:2C:67:64:BA:F8

CODE 2 : NODE 1 : ATtiny1614

The phototransistor connected to it must send DARK to MASTER. MASTER forwards this to NODE 2 which in turn sweeps the servo connected to it. The initial code that ChatGPT gave had if (lightValue > 950). But the reading came out as DIM and DARK with slight variation under room light, and it read 120 values as BRIGHT. When covering it, it read 1024 or above 1000 as DARK. Therefore I changed the value to if (lightValue > 1000) { Serial.println(" DARK"); }

const int PHOTO_PIN = PIN_PA2; //phototransistor conencted to PA2

void setup() {
  Serial.begin(9600); //baud rate
}

void loop() {

  int lightValue = analogRead(PHOTO_PIN); //read the ligthvalue in loop

  Serial.print("Value: "); // print in loop "Value:"
  Serial.print(lightValue); // print the light value 100 or 956

  if (lightValue > 1000) { //if ligth value is more the 1000 print as DARK
    Serial.println("  DARK");
  }
  else if (lightValue > 600) { //if light value more tha 600 print DIM
    Serial.println("  DIM");
  }
  else {
    Serial.println("  BRIGHT"); //orelse  print BRIGHT
  }

  delay(500); //the delay in printing is .5seconds
}
  

The board settings followed are highlighted. Ctrl+Shift+U to upload. The code uploaded without error.


Troubleshooting : The light value was 950, but the phototransistor was reading values around 950 as DIM and DARK.

When covered, the value showed above 100. When light was shown, the reading was around 100.


To get a better steady DARK value, the light value was increased to 1000. Then the DARK and DIM values were stable, without fluctuation due to subtle changes.


CODE 3 : MAC ADDRESS OF MASTER : ESP32-S3

To get the MAC address of MASTER. This way the address is known of both XIAO ESP32 boards.

I got the MAC adress only with the second code #include <esp_wifi.h>This interacts directly with the low-level hardware drivers.

#include <WiFi.h> //library 
#include <esp_wifi.h>  //library

void setup() {
  Serial.begin(115200); //baud rate is 115200
  delay(2000);

  WiFi.mode(WIFI_STA); //turn on wifi hardware
  delay(1000);

  uint8_t mac[6]; //mac address
  esp_wifi_get_mac(WIFI_IF_STA, mac);

  Serial.printf(
    "ESP32-S3 MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n", //the MAC address format
    mac[0], mac[1], mac[2],
    mac[3], mac[4], mac[5]
  );
}

void loop() { //no loop
}
  

MASTER : ESP32-S3 MAC Address: E0:72:A1:D7:7D:64

CODE 4 : MASTER : ESP32-S3

#include <WiFi.h>
#include <esp_now.h>

// NODE 2 ESP32-C6 MAC
uint8_t node2MAC[] = {0x7C, 0x2C, 0x67, 0x64, 0xBA, 0xF8}; //mac address of NODE 2 ESP32-C6 

#define MASTER_TX D6 //tx pin
#define MASTER_RX D7 //rx pin
#define LED_N     D9 //led on master

HardwareSerial Node1Serial(1); 

struct Message { //the message send or received 16 characters
  char text[16];
};

Message incomingMsg; //the incoming message

unsigned long ledOnTime = 0; //
bool ledTimerRunning = false;

// --------------------------------------------------
// Blink MASTER LED twice
// --------------------------------------------------
void blinkMasterLED() { //it pauses all actions and blinks LED twice fro confirmation
  for (int i = 0; i < 2; i++) {
    digitalWrite(LED_N, HIGH);
    delay(500);

    digitalWrite(LED_N, LOW);
    delay(500);
  }
}

// --------------------------------------------------
// Send ESP NOW message to NODE 2
// --------------------------------------------------
void sendToNode2(const char* text) { //add text into "struct Message" and transmits to NODE2
  Message msg;
  strcpy(msg.text, text);

  esp_now_send(node2MAC, (uint8_t*)&msg, sizeof(msg)); //sends the message to NODE 2
}

// --------------------------------------------------
// ESP NOW receive callback
// --------------------------------------------------
void onReceive(const esp_now_recv_info_t *info, //this an intruper trigger when node 2 send a message
               const uint8_t *data,
               int len) {

  memcpy(&incomingMsg, data, sizeof(incomingMsg));

  if (strcmp(incomingMsg.text, "TOUCH") == 0) { //if the incoming message is touch

    Node1Serial.println("LED_ON"); // the led turns high or on

    ledOnTime = millis(); //captures timestamp
    ledTimerRunning = true; //turns on for 

    blinkMasterLED(); // after action confirmation blink
  }
}

void setup() {

  Serial.begin(115200); //baud

  pinMode(LED_N, OUTPUT); //led is output
  digitalWrite(LED_N, LOW); //led is off

  // UART to NODE 1
  Node1Serial.begin(9600, SERIAL_8N1, MASTER_RX, MASTER_TX);

  // ESP NOW
  WiFi.mode(WIFI_STA);

  if (esp_now_init() != ESP_OK) { //Initializes the ESP-NOW wireless protocol,
    Serial.println("ESP NOW Init Failed");
    return;
  }

  esp_now_register_recv_cb(onReceive); //function to listen for incoming data

  esp_now_peer_info_t peerInfo = {};

  memcpy(peerInfo.peer_addr, node2MAC, 6); //pairs the Master explicitly with NODE 2's MAC address
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

  esp_now_add_peer(&peerInfo);

  Serial.println("MASTER READY"); //outputs "MASTER READY"
}

void loop() { //main loop

  // Read UART from NODE 1
  if (Node1Serial.available()) {  //Continuously listens for data coming from NODE 1

    String msg = Node1Serial.readStringUntil('\n');
    msg.trim();

Serial.print("UART Received: "); // to print the message from attiny
Serial.println(msg);

if (msg == "DARK") { //if teh message is dark then follow the below steps

  Serial.println("DARK detected"); //if DARK detected

  sendToNode2("SWEEP"); //do the sweep action

  blinkMasterLED(); //then blik the master led
}
  }

  // Turn LED-A OFF after 1 second
  if (ledTimerRunning) {

    if (millis() - ledOnTime >= 1000) { //if touch was send

      Node1Serial.println("LED_OFF"); //led turns off after 1 second

      ledTimerRunning = false;
    }
  }
}
  

Troubleshooting : The error appeared because of long touchValue = touchRead(TOUCH_PIN);. The error message suggested that I might have meant touchSent. I changed it in the code, but the error still remained. Then ChatGPT explained that touchRead() works on ESP32 and ESP32-S3 touch capable pins, but the ESP32-C6 does not have the same capacitive touch peripheral. I had previously used the touch pad connection successfully, so I added that code and asked ChatGPT to rewrite the code so that the pin would work.

CODE 5 : NODE 2 : ESP32-C6

Functions done by this code: The pins are defined as D0 = MG90S Servo and D6 = Touch Pad. It sends the message "TOUCH" to MASTER when touched on NODE 2. It receives "SWEEP" from MASTER (when the phototransistor prints DARK). It performs one 0° → 180° → 0° sweep. The MAC address used is MASTER MAC: E0:72:A1:D7:7D:64. It also includes WiFi.mode(WIFI_STA); to turn on the Wi-Fi hardware. It is compatible with ESP32 board package 3.3.10.

#include <WiFi.h>
#include <esp_now.h> //esp-now
#include <ESP32Servo.h> // ESP32Servo library

const int SERVO_PIN = D0; //the pins 
const int TOUCH_PIN = D6;
const int TOUCH_THRESHOLD = 2000; // touch threshold adjusted to 2000

Servo myServo;

// MASTER ESP32-S3 MAC
uint8_t masterMAC[] = { // MAC ADRESS MASTER ESP32-S3
  0xE0, 0x72, 0xA1,
  0xD7, 0x7D, 0x64
};

struct Message { //the text box
  char text[16];
};

Message incomingMsg;

bool touchSent = false;

// --------------------------------------------------
// Servo Sweep
// --------------------------------------------------
void servoSweep() { //if touch is detected 

  myServo.write(0); //with delay of .3seconds at 0
  delay(300);

  myServo.write(180); //with delay of 1 second at 180degree sweep
  delay(1000);

  myServo.write(0);
  delay(1000);
}

// --------------------------------------------------
// Send ESP-NOW Message
// --------------------------------------------------
void sendToMaster(const char* text) { //to send text

  Message msg;
  strcpy(msg.text, text);

  esp_now_send(masterMAC, (uint8_t*)&msg, sizeof(msg));
}

// --------------------------------------------------
// Receive Callback
// --------------------------------------------------
void onReceive(const esp_now_recv_info_t *info,
               const uint8_t *data,
               int len) {

  memcpy(&incomingMsg, data, sizeof(incomingMsg)); //the incoming data

  if (strcmp(incomingMsg.text, "SWEEP") == 0) {

    Serial.println("SWEEP RECEIVED"); //when sweep i noted

    servoSweep();
  }
}

void setup() {

  Serial.begin(115200);

  myServo.attach(SERVO_PIN); // the servo pin has to move
  myServo.write(0);

  WiFi.mode(WIFI_STA);

  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP-NOW Init Failed");
    return;
  }

  esp_now_register_recv_cb(onReceive);

  esp_now_peer_info_t peerInfo = {};

  memcpy(peerInfo.peer_addr, masterMAC, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

  esp_now_add_peer(&peerInfo);

  Serial.println("NODE2 READY"); //prints node 2 readyw ehn uploaded
}

void loop() {

pinMode(TOUCH_PIN, OUTPUT);
digitalWrite(TOUCH_PIN, LOW);
delayMicroseconds(10);

pinMode(TOUCH_PIN, INPUT);

int count = 0;

while (digitalRead(TOUCH_PIN) == LOW) {
  count++;
}

Serial.print("Touch Value: ");
Serial.println(count);

if (count < TOUCH_THRESHOLD) {

  if (!touchSent) {

    Serial.println("TOUCH DETECTED"); //when count cycle is less than threshold value

    sendToMaster("TOUCH");

    touchSent = true;
  }
}
else {

  touchSent = false;
}
  delay(50);
}
  

When I cover the phototransistor on NODE 1, the value goes above 1000. This triggers DIM. NODE 1, connected to the MASTER, sends the DIM signal. The MASTER transmits this value to NODE 2 as DIM, and it performs the SWEEP action. Once the action is complete, the LED on the MASTER blinks twice.
On NODE 2, when the touch pad is touched, NODE 2 sends the TOUCHED signal to the MASTER. This triggers NODE 1 to turn the LED on and then off. After this is done, the LED on the MASTER blinks twice.

The first action is interaction with the touch pad on NODE 2. The LED on NODE 1 lights up, and after that, the MASTER LED blinks twice to denote that the action is complete.
The second action is covering the phototransistor on NODE 1. The servo on NODE 2 sweeps, and the LED on the MASTER blinks twice after that.

Communication is not just a single way process. For better interaction, it is always good to communicate in both directions. NODE 1 speaks to NODE 2 through the MASTER. NODE 2 also speaks to NODE 1 through the MASTER. When NODE 1 and NODE 2 communicate, the MASTER also acknowledges it by blinking.


BLE : RTC + SERVO

Communication flow. The communication does not meet the assignment conditions. It needs reverse communication as well.

The conversation was followed by interaction with Gemini Interaction with Gemini

 
i need two xiao boardS to communincate with eachother by ESP-NOW. CREATE SIMPLE ARDUINO CODES. XIAO ESP32C6 IS THE SENDER, HEREAFTER BE CALLED C6 XIAO ESP32S3 IS THE RECEIVER, HEREAFTER BE CALLED S3 RECEIVER MAC ID IS 30:30:f9:34:60:2c USE 115200 BAUD WHEN I PRESS THE TOUCH PAD (PIN D6) ON C6 BOARD . IN S3 BOARD I NEED THE LED CONNECTED TO PIN D9 TO TURN ON. Touched ≈ 70 Not touched ≈ 1000 TOUCH ARDUINO CODE: #define TOUCH_PIN D6 int threshold = 500; // adjust based on your readings void setup() { Serial.begin(115200); } void loop() { // Discharge pin pinMode(TOUCH_PIN, OUTPUT); digitalWrite(TOUCH_PIN, LOW); delayMicroseconds(10); // Switch to input and measure charge time pinMode(TOUCH_PIN, INPUT); int count = 0; while (digitalRead(TOUCH_PIN) == LOW) { count++; } Serial.print("Value: "); Serial.println(count); // Your case: touched = LOWER value if (count < threshold) { Serial.println("TOUCHED"); } else { Serial.println("NOT TOUCHED"); } Serial.println("------------------"); delay(1500); // slower (1.5 seconds) } LED BLINK TEST CODE: #define LED_PIN D9 // Define pin D9 void setup() { pinMode(LED_PIN, OUTPUT); // Set D9 as output } void loop() { digitalWrite(LED_PIN, HIGH); // Turn LED ON delay(1000); // Wait 1 second digitalWrite(LED_PIN, LOW); // Turn LED OFF delay(1000); // Wait 1 second } WRITE CODES REFERING THE ABOVE TO AVOID ERRORS. GIVE SIMPLE , CLEAN SENDER AND RECEIVER CODES, WITHOUT ERROR
 

ATtiny1614 - Input week

"I asked AI to program it so that the RTC would display the time in seconds when connected." Well, I wish that was actually what I did. I did not understand a lot of what was happening, and most of the code I got was messy. The communication between the boards was not working. After a lot of back and forth with ChatGPT and Gemini, I eventually got the code working. I have included one such interaction where I got the code. It is a lengthy conversation: Interaction with Gemini.

What I would suggest doing next time is programming each action separately, testing it, and then gradually adding more functionality to the code later.

Programming the ATtiny1614 using a programmer. I am reusing an image from Week 9 to show the connection, as it is the same setup.

Library needed: RTClib by Adafruit.
This code reads data from the RTC using I2C and sends the seconds count to the ESP32-S3 via UART. I upload this code first. Since there is no coin cell battery on the RTC, the time starts from 0 as soon as it is plugged in.


        #include <Wire.h>
#include "RTClib.h"

RTC_DS3231 rtc;

void setup() {
  // ATtiny1614 Serial (TX:PB2, RX:PB3)
  Serial.begin(9600); 
  Wire.begin(); // SCL:PB0, SDA:PB1

  if (!rtc.begin()) {
    while (1); // Stop if RTC not found
  }
}

void loop() {
  DateTime now = rtc.now();
  
  // We send just the "Seconds" as a string to simplify the trigger
  Serial.println(now.second()); 
  
  delay(1000); 
}
    

The master listens to and receives data from Node 1 (ATtiny1614) via UART and "broadcasts" that value over BLE.


    #include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

BLECharacteristic *pCharacteristic;
bool deviceConnected = false;

// We need a class to handle connection status
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) { deviceConnected = true; };
    void onDisconnect(BLEServer* pServer) { 
      deviceConnected = false; 
      BLEDevice::startAdvertising(); // Restart advertising when disconnected
      Serial.println("Device disconnected, restarting advertising...");
    }
};

void setup() {
  Serial.begin(115200);
  // On S3, Serial1 pins are 44 (RX) and 43 (TX)
  Serial1.begin(9600, SERIAL_8N1, 44, 43); 

  Serial.println("Initializing S3 BLE...");

  BLEDevice::init("XIAO_S3_SENDER");
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  BLEService *pService = pServer->createService(SERVICE_UUID);

  pCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID,
                      BLECharacteristic::PROPERTY_READ   |
                      BLECharacteristic::PROPERTY_WRITE  |
                      BLECharacteristic::PROPERTY_NOTIFY |
                      BLECharacteristic::PROPERTY_INDICATE
                    );

  pService->start();

  // Explicitly setup advertising
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  
  pAdvertising->setMinPreferred(0x12);
  
  BLEDevice::startAdvertising();
  Serial.println("S3 is now advertising! Check your phone now.");
}

void loop() {
  // Check if ATtiny sent data
  if (Serial1.available()) {
    String tinyData = Serial1.readStringUntil('\n');
    tinyData.trim(); // Remove any hidden spaces/newlines
    
    if (tinyData.length() > 0) {
      pCharacteristic->setValue(tinyData.c_str());
      pCharacteristic->notify(); 
      Serial.print("Data from ATtiny sent to BLE: ");
      Serial.println(tinyData);
    }
  }
  
  // Heartbeat to Serial Monitor so you know the S3 hasn't crashed
  static unsigned long lastTime = 0;
  if (millis() - lastTime > 5000) {
    Serial.println("S3 is alive and waiting for ATtiny data...");
    lastTime = millis();
  }
}

This code runs on a XIAO ESP32-C6 acting as a BLE receiver. It scans for and connects to a BLE sender, receives RTC seconds data, and monitors incoming values. Whenever the received seconds value is a multiple of 5, the servo connected to D0 rotates to 180° and returns to 0°.


#include <BLEDevice.h>
#include <ESP32Servo.h>


#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" //The sender and receiver must use the same UUIDs to communicate.

Servo myServo; //UUID (Universally Unique Identifier) is a unique ID used in BLE to identify services and characteristics.
int servoPin = 0; // D0 on C6
static BLEUUID binServiceUUID(SERVICE_UUID);
static BLEUUID binCharUUID(CHARACTERISTIC_UUID);
bool doConnect = false;
BLERemoteCharacteristic* pRemoteChar;
BLEAdvertisedDevice* myDevice;

// --- UPDATED SERVO FUNCTION WITH REAL-TIME MONITORING ---
void sweepServo() {
  Serial.println(">>> 5 SECOND MARK DETECTED!");
  
  Serial.print("Moving to 180°... "); //moves 180 degree
  myServo.write(180); 
  Serial.println("[DONE]");
  
  delay(500); // Wait for the motor to physically reach the spot waits 0.5seconds
  
  Serial.print("Returning to 0°... "); //returns to 0 degree
  myServo.write(0);
  Serial.println("[DONE]");
  
  Serial.println("Sweep Cycle Complete. Waiting for next 5s mark...\n"); 
}

static void notifyCallback(BLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
  String val = "";
  for (int i = 0; i < length; i++) val += (char)pData[i];
  
  int seconds = val.toInt();
  Serial.print("RTC Time Received: "); 
  Serial.print(seconds);
  Serial.println("s");

  // Trigger sweep every 5 seconds
  if (seconds % 5 == 0 && seconds != 0) {
    sweepServo();
  }
}

class MyCallbacks: public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    if (advertisedDevice.isAdvertisingService(binServiceUUID)) {
      BLEDevice::getScan()->stop();
      myDevice = new BLEAdvertisedDevice(advertisedDevice);
      doConnect = true;
    }
  }
};

void setup() {
  Serial.begin(115200);
  myServo.attach(servoPin);
  myServo.write(0); // Initialize at zero
  
  BLEDevice::init("XIAO_C6_RECEIVER");
  BLEScan* pScan = BLEDevice::getScan();
  pScan->setAdvertisedDeviceCallbacks(new MyCallbacks());
  pScan->setActiveScan(true);
  pScan->start(30);
  
  Serial.println("C6 Receiver Started. Scanning for S3 Sender...");
}

void loop() {
  if (doConnect) {
    BLEClient* pClient = BLEDevice::createClient();
    if(pClient->connect(myDevice)) {
      Serial.println("--- CONNECTED TO S3 SENDER ---");
      BLERemoteService* pService = pClient->getService(binServiceUUID);
      if (pService != nullptr) {
        pRemoteChar = pService->getCharacteristic(binCharUUID);
        if (pRemoteChar != nullptr) {
           pRemoteChar->registerForNotify(notifyCallback);
           Serial.println("Monitoring RTC data...");
        }
      }
    } else {
      Serial.println("Connection failed. Retrying scan...");
      BLEDevice::getScan()->start(30);
    }
    doConnect = false;
  }
}

The RTC on the node 1 sends time to Master through UART which prints the seconds on the serial monitor. Through BLE the servo on node 2 does a 180 degree sweep every 5 seconds.


Group Assignment

We communcated with 2 projects. Nadec's and Mishael's

For this week's group assignment, we established communication between two different project's boards using both Wi Fi (TCP) and ESP NOW. Nadec's custom ESP32 board acted as Master - a Wi Fi access point and TCP server. It had six push buttons and an OLED display. Using the buttons, text could be typed on a QWERTY keyboard displayed on the OLED screen.
Mishael's setup consisted of two XIAO ESP32 C6 boards. these were the nodes. The first node connected to Nadec's Wi Fi network and received the text through TCP communication. This node then forwarded the received message to a second XIAO ESP32 C6 node using ESP NOW.
The second node was connected to an MP3 player module and speaker. When it received the message, it played the corresponding audio file through the speaker. The complete communication flow was:
Text entered on Nadec's OLED screen → sent through Wi Fi using TCP → received by Mishael's node 1 → forwarded through ESP NOW → received by the node 2 → audio played through the speaker.
Through this assignment, we learned how different communication protocols can be combined in a single system to transfer data between multiple nodes and trigger an output device.

Link to group assignment

REFERENCES:

XIAO ESP32C6
ESP-NOW David Fernandez -Fab academy
Kerstin Ogrissek - Fab Academy
SPI PCB Interfaces


Download Week 11 Zip File


AI INTERACTION

Step by step with simple short explanation- ChatGPT Touch +LED - Chatgpt