Code:
/*
* Copyright (c) 2024 KinCony IoT (https://www.kincony.com)
*
* This Arduino program implements communication between ESP32-S3 and the Tuya module
* via UART (serial communication). It listens for specific packets from the Tuya module
* and responds according to predefined commands. Additionally, it uses an LED connected
* to GPIO45 to indicate the network status based on the Tuya module's response, and a button
* on GPIO21 to initiate a manual quick configuration (EZ mode).
*
* Functionality:
* 1. The ESP32-S3 communicates with the Tuya module via serial (UART).
* 2. The program listens for specific commands from the Tuya module, such as heartbeat, product info requests, work mode requests, and network status.
* 3. When the ESP32-S3 receives a heartbeat packet (0x55 0xAA 00 00 00 00 0xFF), it responds with a heartbeat response (0x55 0xAA 03 00 00 01 00 03).
* 4. When the ESP32-S3 receives a network status update, it controls an LED connected to GPIO45 to indicate the current state:
* - Fast blink (every 250ms) indicates the device is in quick configuration mode (EZ mode).
* - Slow blink (every 1500ms) indicates the device is in hotspot configuration mode (AP mode).
* - Solid ON indicates the device is successfully connected to the cloud.
* 5. A button connected to GPIO21 is used to send a command to the Tuya module to start the quick configuration mode.
*
* Pin definitions:
* - TXD (ESP32 to Tuya module): GPIO15
* - RXD (Tuya module to ESP32): GPIO16
* - LED for network status indication: GPIO45
* - Button for starting quick configuration: GPIO21
*/
#include <HardwareSerial.h>
// Create a HardwareSerial object for UART communication on ESP32
HardwareSerial tuyaSerial(1);
// Define the GPIO pins for TXD and RXD used for serial communication with the Tuya module
#define TXD_PIN 15
#define RXD_PIN 16
// Set the baud rate for Tuya module communication to 9600
#define BAUD_RATE 9600
// Define the GPIO for LED and Button
#define LED_PIN 45
#define BUTTON_PIN 21
// Define LED flash intervals (in milliseconds) for different states
#define FAST_FLASH_INTERVAL 250 // Fast blink for EZ mode
#define SLOW_FLASH_INTERVAL 1500 // Slow blink for AP mode
#define LONG_LIGHT_INTERVAL 10000 // Long light for cloud connection (simulating always ON)
// Variables to control LED state
bool ledState = LOW; // Tracks the current state of the LED
unsigned long previousMillis = 0; // Used to track time for blinking the LED
unsigned long flashInterval = LONG_LIGHT_INTERVAL; // Default interval (long ON)
// Define the response packets for different commands from the Tuya module
// Heartbeat response: Tuya expects this response after sending a heartbeat
uint8_t heartBeatResponse[] = {0x55, 0xAA, 0x03, 0x00, 0x00, 0x01, 0x00, 0x03};
// Product info response with details (e.g., product ID, firmware version, etc.)
uint8_t productInfoResponse[] = {
0x55, 0xAA, 0x03, 0x01, 0x00, 0x2A, 0x7B, 0x22, 0x70, 0x22, 0x3A, 0x22,
0x63, 0x68, 0x6D, 0x7A, 0x6C, 0x67, 0x6A, 0x70, 0x61, 0x64, 0x70, 0x71,
0x78, 0x64, 0x6B, 0x6F, 0x22, 0x2C, 0x22, 0x76, 0x22, 0x3A, 0x22, 0x31,
0x2E, 0x30, 0x2E, 0x30, 0x22, 0x2C, 0x22, 0x6D, 0x22, 0x3A, 0x30, 0x7D, 0xAA
};
// Work mode response: Sent when the Tuya module asks for the current work mode
uint8_t workModeResponse[] = {0x55, 0xAA, 0x03, 0x02, 0x00, 0x00, 0x04};
// Network status response: Sent when the Tuya module asks for the network status
uint8_t netStatusResponse[] = {0x55, 0xAA, 0x03, 0x03, 0x00, 0x00, 0x05};
// Subsequent heartbeat response: Sent after the first heartbeat response
uint8_t secondHeartBeatResponse[] = {0x55, 0xAA, 0x03, 0x00, 0x00, 0x01, 0x01, 0x04};
// Command to start quick configuration (EZ mode) when the button is pressed
uint8_t quickConfigCommand[] = {0x55, 0xAA, 0x03, 0x05, 0x00, 0x01, 0x00, 0x08};
void setup() {
// Initialize the serial communication for debugging at 115200 baud rate
Serial.begin(115200);
// Initialize the serial communication with Tuya module at 9600 baud rate
tuyaSerial.begin(BAUD_RATE, SERIAL_8N1, RXD_PIN, TXD_PIN);
// Initialize the GPIO for LED and button
pinMode(LED_PIN, OUTPUT); // Set the LED pin as output
pinMode(BUTTON_PIN, INPUT_PULLUP); // Use internal pull-up resistor for the button
// Debug message to indicate that the serial communication has been initialized
Serial.println("ESP32-Tuya serial communication initialized.");
}
void loop() {
// Handle LED blinking based on the current flash interval
unsigned long currentMillis = millis(); // Get the current time
if (currentMillis - previousMillis >= flashInterval) {
previousMillis = currentMillis; // Update the time tracker
ledState = !ledState; // Toggle the LED state
digitalWrite(LED_PIN, ledState); // Update the LED state
}
// Check if the button is pressed (active low)
if (digitalRead(BUTTON_PIN) == LOW) {
Serial.println("Button pressed, sending quick config command...");
sendPacket(quickConfigCommand, sizeof(quickConfigCommand)); // Send quick config command
delay(500); // Debounce delay to avoid multiple triggers
}
// Check if there is any data available from the Tuya module
if (tuyaSerial.available()) {
uint8_t incomingPacket[7]; // Array to store the received packet
size_t bytesRead = tuyaSerial.readBytes(incomingPacket, 7); // Read 7 bytes from Tuya module
// Check if the packet has a valid header (0x55, 0xAA)
if (bytesRead >= 2 && incomingPacket[0] == 0x55 && incomingPacket[1] == 0xAA) {
// If less than 7 bytes were received, wait for more data
if (bytesRead < 7) {
delay(50); // Wait briefly to receive the remaining bytes
while (tuyaSerial.available()) {
incomingPacket[bytesRead++] = tuyaSerial.read(); // Continue reading remaining bytes
if (bytesRead >= 7) break;
}
}
// If the packet is still incomplete, discard it
if (bytesRead < 7) return;
// Process the received packet
processTuyaPacket(incomingPacket, 7);
} else {
// If the header is invalid, discard the packet
tuyaSerial.flush(); // Clear the serial buffer
}
}
// Small delay to prevent CPU overload
delay(100);
}
// Function to process the received packet from Tuya module and send appropriate responses
void processTuyaPacket(uint8_t* packet, size_t size) {
// Ensure the packet size is correct and the header is valid
if (size == 7 && packet[0] == 0x55 && packet[1] == 0xAA) {
// Check the command byte (packet[2]) to determine the type of request
switch (packet[2]) {
case 0x00:
// Heartbeat request
if (packet[3] == 0x00 && packet[4] == 0x00 && packet[5] == 0x00 && packet[6] == 0xFF) {
Serial.println("Heartbeat received.");
sendPacket(heartBeatResponse, sizeof(heartBeatResponse)); // Send heartbeat response
}
break;
case 0x01:
// Product info request
Serial.println("Product info request received.");
sendPacket(productInfoResponse, sizeof(productInfoResponse)); // Send product info response
break;
case 0x02:
// Work mode request
Serial.println("Work mode request received.");
sendPacket(workModeResponse, sizeof(workModeResponse)); // Send work mode response
break;
case 0x03:
// Network status request
Serial.println("Network status request received.");
sendPacket(netStatusResponse, sizeof(netStatusResponse)); // Send network status response
// Change LED flash pattern based on network status (packet[6])
if (packet[6] == 0x00) {
flashInterval = FAST_FLASH_INTERVAL; // Set LED to fast blink for EZ mode
} else if (packet[6] == 0x01) {
flashInterval = SLOW_FLASH_INTERVAL; // Set LED to slow blink for AP mode
} else if (packet[6] == 0x04) {
flashInterval = LONG_LIGHT_INTERVAL; // Set LED to long ON for cloud connection
digitalWrite(LED_PIN, HIGH); // Ensure LED is ON
}
break;
default:
Serial.println("Error: Unhandled command received.");
break;
}
}
}
// Function to send a response packet to the Tuya module
void sendPacket(uint8_t* packet, size_t size) {
// Send the packet via UART to the Tuya module
tuyaSerial.write(packet, size);
// Debug: Print the sent packet for logging purposes
Serial.print("Sent packet: ");
for (size_t i = 0; i < size; i++) {
Serial.print(packet[i], HEX);
Serial.print(" ");
}
Serial.println();
}
arduino ino file download: