yahboom
HomeLearn
Solution
Blog
Online Store
About Us
EN/中文
ESP32 Core Board📖
1. ESP32 Introduction
  • 1. ESP32S3 Overview
  • 2. Environment Setup Introduction
  • 3. Dev Board Introduction
2. ESP-IDF Development Tutorial
1. Before Development
  • 1. Environment Setup (Windows)
  • 2. Environment Setup (Linux)
  • 3. Build and Flash
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. ESP_TIMER
  • 5. GPTIMER
  • 6. Watchdog Timer
  • 7. DMA
  • 8. ADC
  • 9. PWM Breathing LED
  • 10. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication Experiment
  • 2. I2C_OLED Display Experiment
  • 3. SPI Bus Communication Experiment
  • 4. PSRAM External Memory Experiment
  • 5. Flash Read and Write Experiment
  • 6. USB Emulated Mass Storage Experiment
4. FreeRTOS operating system
  • 1.FreeRTOS Core Concepts Theory
  • 2.Task Creation and Management
  • 3.Queue
  • 4.Binary Semaphore
  • 5.Mutex
  • 6.Event Groups
  • 7.Software Timer
  • 8.Task Scheduler
  • 9.Low Power Mode
  • 10.Deadlock
  • 11.Priority Inversion
5. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Client Experiment
  • 5. UDP Server Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. Socket Programming Basics
  • 9. HTTP Client Experiment
  • 10. HTTP Server Experiment
  • 11. MQTT IoT Communication Protocol
  • 12. WIFI OTA Upgrade
  • 13. Bluetooth Fundamentals
  • 14. Bluetooth Slave Mode
  • 15. Bluetooth Master Mode
6. Audio Applications
  • 1.I2S Microphone Recording and U-Disk Playback
  • 2.I2S Audio Playback Experiment
  • 3.Repeater Experiment
7. Xiaozhi AI Voice
  • 1. Burn Xiaozhi Firmware
  • 2. Network and Configuration
  • 3. AI Agent Settings
  • 4. Program Analysis
  • 5. Free Chat
3. Arduino Development Course
1. Before Development
  • 1. Environment Setup
  • 2. Build and Flash
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. ESP_TIMER
  • 5. GPTIMER
  • 6. Watchdog Timer
  • 7. ADC
  • 8. PWM Breathing LED
  • 9. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication
  • 2. I2C OLED Display
  • 3. SPI Bus Communication
  • 4. Flash Read and Write
  • 5. USB Emulated Mass Storage
4. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Server Experiment
  • 5. UDP Client Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. HTTP Client Experiment
  • 9. HTTP Server Experiment
  • 10. MQTT IoT Communication Protocol
  • 11. WIFI OTA Upgrade
  • 12. Bluetooth Fundamentals
  • 13. Bluetooth Slave Mode
  • 14. Bluetooth Master Mode
5. Audio Applications
  • 1.I2S Microphone Recording and U-Disk Playback
  • 2.I2S Audio Playback Experiment
  • 3.Repeater Experiment
4. MicroPython Development Course
1. Before Development
  • 1. Environment Setup
  • 2. Build and Flash
  • 3. Thonny IDE
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. TIMER_IT
  • 5. Watchdog Timer
  • 6. ADC
  • 7. PWM Breathing LED
  • 8. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication Experiment
  • 2. I2C_OLED Display Experiment
  • 3. SPI Bus Communication Experiment
  • 4. Flash Read and Write Experiment
  • 5. Print Storage
4. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Client Experiment
  • 5. UDP Server Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. HTTP Client Experiment
  • 9. HTTP Server Experiment
  • 10. MQTT
  • 11. Bluetooth Fundamentals
  • 12. Bluetooth Slave Mode
  • 13. Bluetooth Master Mode
5. Audio Applications
  • 1. I2S Audio Playback Experiment
  • 2. Repeater Experiment
Download
  • Code
  • Hardware_Info
  • 3D_Model_File
  • Software_Tools
  • VMware
  • VM_File
Download ZIP

Welcome to ESP32 Core Board repository

8. Socket Programming Basics

Socket Programming Basics

Socket Programming Basics


Socket Programming Basics1. Experiment Overview2. Core Concepts2.1 Custom Protocol Packet StructurePack Example: "Hello YAHBOOM" (13-byte payload)2.2 length Field Calculation2.3 Checksum Calculation (ICMP Style)Calculation Steps (ESP32 Little-Endian View)C Code ImplementationPack Call FlowReceive Packet Verification Flow2.4 Network Byte Order3. Project Architecture and Flow4. Hardware Dependencies5. Source Code Analysis5.1 Header Files and Macro Configuration5.2 WiFi Initialization and AP Scan5.3 wifi_handler() — With AP Scan5.4 socket_experiment() — Pack/Send/Receive/Verify5.6 Key Points of Pack Flow6. Operation Flow6.1 Build and Flash Flow6.2 Network Debugging Assistant Configuration6.3 Pack Tool6.4 Normal Log and Step Breakdown7. Common Problems


1. Experiment Overview

Custom binary protocols are one of the most core and fundamental skills in embedded IoT development. Whether smart home devices report temperature/humidity data, industrial sensors push device status regularly, or OTA firmware upgrade package distribution and verification—all rely on custom protocol packet assembly and unpacking. Mastering this skill means you can make devices exchange binary data in the most efficient, compact, and reliable way, rather than depending on redundant JSON or plain text strings.

This experiment introduces custom binary protocols on top of TCP Socket communication, demonstrating the most core skill in embedded communication—Packet Assembly (Pack) and Unpack.

In actual IoT products, raw string transmission is inefficient, insecure, and has no verification. Custom binary protocols precisely define each frame's data format, using Checksum to ensure data integrity. Main application scenarios:

  • Multi-sensor data reporting: Pack temperature, humidity, pressure and other sensor data into one binary frame, send at once, saving bandwidth and power.

  • Inter-device command interaction: Upper computer sends control commands (on/off/speed adjust), device parses and executes, replies with acknowledgment frame—frame header + type + checksum ensures no command misjudgment.

  • OTA firmware upgrades: Upgrade packages are numbered and packed in fixed-size chunks, each chunk with checksum, ensuring firmware files are not corrupted during transmission.

  • Private IoT protocols: Most IoT manufacturers use custom binary protocols to connect to cloud platforms—more traffic-efficient and secure than MQTT+JSON.

ComparisonPlain String ProtocolCustom Binary Protocol
Data densityLow ("temp:25.5" = 9 bytes)High (float 4 bytes)
Parsing complexitySimple, string processing neededFixed offset direct reading
Verification capabilityUsually noneICMP-style checksum
ExtensibilityString format changes need parsing updatesAdding fields only extends struct
Typical applicationsDebugging, simple controlMass production, multi-sensor data reporting

This project uses ESP32 as TCP client. After WiFi connection, scan nearby APs → connect to specified WiFi → create TCP Socket → build 72-byte custom protocol packet → send → receive reply → verify and unpack.

Course arrangement note: This chapter places Socket custom protocols after the TCP tutorial because the TCP tutorial has already fully covered Socket programming basics (socket(), connect(), send(), recv(), close()). This experiment won't repeat these basic API explanations but focuses on higher-level skills—how to design binary protocol struct, how to calculate checksum, how to pack and unpack. The TCP tutorial solves "how to transmit", this experiment solves "what to transmit, how to verify what was transmitted".


2. Core Concepts

2.1 Custom Protocol Packet Structure

The protocol packet consists of header (8 bytes) + payload (max 64 bytes). Header is fixed at 8 bytes, actual frame length = 8 + length, ultimately no more than 72 bytes.

┌────────┬─────────┬──────┬────────┬──────────┬────────────────┐
│ header │ version │ type │ length │ checksum │   data[64]     │
│ 1B     │ 1B      │ 2B   │ 2B     │ 2B       │ max 64B       │
│ 0xAA   │ 0x01    │      │        │          │                │
└────────┴─────────┴──────┴────────┴──────────┴────────────────┘
                       Header 8 Bytes         Payload = length bytes
FieldOffsetSizeDescription
header01BFrame header, fixed 0xAA, first verification step when receiving
version11BProtocol version number
type22BMessage type (big-endian), e.g. 0x0001 = query request
length42BPayload byte count (big-endian), max 64
checksum62BICMP-style checksum of header + payload (big-endian)
data80~64BPayload data, empty when length is 0

C struct definition (__attribute__((packed)) forbids compiler padding/alignment bytes):

xxxxxxxxxx
typedef struct {
    uint8_t  header;    // Frame header 0xAA
    uint8_t  version;   // Version number
    uint16_t type;      // Message type (network byte order)
    uint16_t length;    // Data length (network byte order)
    uint16_t checksum;  // Checksum (network byte order)
    char     data[64];  // Payload data
} __attribute__((packed)) proto_pkt_t;

Pack Example: "Hello YAHBOOM" (13-byte payload)

Sending "Hello YAHBOOM" (13 bytes), complete protocol packet is 21 bytes, hex breakdown:

xxxxxxxxxx
AA 01 00 01 00 0D F4 2A 48 65 6C 6C 6F 20 59 41 48 42 4F 4F 4D
│  │  └──┬──┘ └──┬──┘ └──┬──┘ └──────────────┬──────────────┘
│  │    type   length  checksum             data
│  │   2B BE   2B BE  LE 2B            13B ASCII
│  version
│   0x01
header
  0xAA
OffsetByteFieldValueMeaning
0AAheader0xAAFrame header identifier
101version1Protocol version
2-300 01type0x0001Query request (big-endian)
4-500 0Dlength13Payload 13 bytes (big-endian)
6-7F4 2Achecksum0xF42AChecksum (ESP32 little-endian, ntohs displays)
8-2048 65 ... 4Ddata"Hello YAHBOOM"Payload ASCII

Key points:

  • length field determines actual frame length = 8 + length. ESP32 receives according to this byte count, stops when full

  • data[64] is max capacity, actually only uses first length bytes, remaining part is auto-zeroed by memset

  • When sending from network debugging assistant, only fill to 8 + length bytes, no need to pad to 72

  • checksum coverage = 8-byte header + length bytes payload, does not include padding zeros

2.2 length Field Calculation

The length field stores the actual byte count of payload data[] (excluding 8-byte header). Obtained via strlen() then converted to big-endian:

xxxxxxxxxx
strcpy(pkt.data, "Hello YAHBOOM");
pkt.length = htons((uint16_t)strlen(pkt.data));  // length = 0x000D (13, big-endian)

Whatever length the sender fills, the receiver receives exactly 8 + length bytes and stops when full. Max is limited by data[64] to 64 bytes, but sender and receiver negotiate actual frame length via length—no need to send full.

2.3 Checksum Calculation (ICMP Style)

Checksum detects bit errors in data transmission, coverage = 8-byte header + length bytes payload.

Calculation Steps (ESP32 Little-Endian View)

Example with "Hello YAHBOOM", 21 bytes of raw data (checksum position filled with 00 00 first):

xxxxxxxxxx
AA 01 00 01 00 0D 00 00 48 65 6C 6C 6F 20 59 41 48 42 4F 4F 4D

Important: ESP32 is a little-endian CPU. calc_checksum((uint16_t*)&pkt, len) reads each pair of bytes in little-endian order. That is, memory AA 01 is read as uint16_t value 0x01AA (low byte first).

Step 1: Little-endian 16-bit grouped accumulation

xxxxxxxxxx
0x01AA + 0x0100 + 0x0D00 + 0x0000 + 0x6548 + 0x6C6C + 0x206F + 0x4159 + 0x4248 + 0x4F4F + 0x004D
                                                                                          ↑
                                                                           Odd byte, high 8 bits padded with 0
= 0x1D50A

Step 2: Carry fold (twice)

​x
0x1D50A → sum = (sum >> 16) + (sum & 0xFFFF)
        = 0x0001 + 0xD50A
        = 0xD50B

0xD50B  → sum = sum + (sum >> 16)
        = 0xD50B + 0x0000
        = 0xD50B

Step 3: Bitwise NOT

xxxxxxxxxx
~0xD50B = 0x2AF4

Checksum host order value = 0x2AF4, stored in memory in little-endian = low byte first → F4 2A. Log ntohs(checksum) shows 0xF42A.

C Code Implementation

xxxxxxxxxx
static uint16_t calc_checksum(uint16_t *buf, int len)
{
    uint32_t sum = 0;
    while (len > 1) { sum += *buf++; len -= 2; }
    if (len == 1) sum += *(uint8_t *)buf;
    sum = (sum >> 16) + (sum & 0xffff);
    sum += sum >> 16;
    return (uint16_t)(~sum);
}

Pack Call Flow

xxxxxxxxxx
proto_pkt_t pkt;
memset(&pkt, 0, sizeof(pkt));
pkt.header  = 0xAA;
pkt.version = 1;
pkt.type    = htons(0x0001);
strcpy(pkt.data, "Hello YAHBOOM");
pkt.length  = htons((uint16_t)strlen(pkt.data));
pkt.checksum = 0;
pkt.checksum = calc_checksum((uint16_t *)&pkt,
                              8 + ntohs(pkt.length));

Receive Packet Verification Flow

xxxxxxxxxx
Save received checksum → Clear checksum field → Recalculate calc_checksum() for (8 + length) bytes
→ Compare two checksums → Match = verified, mismatch = data corrupted

2.4 Network Byte Order

Multi-byte fields (type, length, checksum) use big-endian (network byte order) uniformly on the network:

xxxxxxxxxx
pkt.type    = htons(0x0001);     // Host order → Network order
pkt.length  = htons(strlen(data));

// Receiver converts back to host order
uint16_t type = ntohs(pkt.type);

3. Project Architecture and Flow

xxxxxxxxxx
app_main()
  ├─ wifi_init()
  │   ├─ nvs_flash_init()
  │   ├─ esp_netif_init()
  │   ├─ esp_event_loop_create_default()
  │   ├─ xEventGroupCreate()
  │   ├─ esp_netif_create_default_wifi_sta()
  │   ├─ esp_wifi_init() + register event callback
  │   └─ esp_wifi_set_mode(STA) + esp_wifi_start()
  │
  ├─ wifi_connect()
  │   └─ Wait for WiFi to get IP (WIFI_OK, 30s timeout)
  │       └─ wifi_handler():
  │           ├─ WIFI_EVENT_STA_START  → esp_wifi_scan_start()    Scan APs
  │           ├─ WIFI_EVENT_SCAN_DONE  → Print AP list → Connect to target WiFi
  │           ├─ WIFI_EVENT_STA_DISCONNECTED → Auto reconnect
  │           └─ IP_EVENT_STA_GOT_IP   → Set WIFI_OK, print IP + gateway
  │
  └─ socket_experiment()
      ├─ 1. socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) → Create TCP socket
      ├─ 2. connect(192.168.11.183:8080)              → Three-way handshake
      ├─ 3. Build proto_pkt_t struct (fill fields → clear checksum → calculate & write)
      ├─ 4. send() send protocol packet (8 + length bytes)
      └─ 5. while(1) loop receive packets → header verify → checksum unpack → continue


4. Hardware Dependencies

  • Hardware Connection: No external wiring, WiFi wireless communication

  • Required Components: lwip/sockets, esp_wifi, esp_netif, esp_event, nvs_flash, FreeRTOS


5. Source Code Analysis

Full source: Source Code → ESP-IDF → 5.Network_Protocol → 5.Socket

5.1 Header Files and Macro Configuration

This experiment introduces LWIP Socket API, ESP32 WiFi driver, and FreeRTOS event groups. Defines three macros for WiFi connection, server address, and max scan count. WiFi hotspot and server IP/port can be modified per actual environment.

xxxxxxxxxx
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "lwip/sockets.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "nvs_flash.h"

static const char *TAG = "SOCKET";
#define WIFI_SSID      "Your_WiFi_SSID"
#define WIFI_PASS      "Your_WiFi_Password"
// Server IP
#define SERVER_IP      "192.168.11.183"
// Server port
#define SERVER_PORT    8080
// Max APs to scan
#define MAX_SCAN       32

5.2 WiFi Initialization and AP Scan

Terminology distinction: In this section, "AP" refers to Wireless Access Point—the WiFi router that ESP32 as STA (client) connects to, not ESP32's AP mode (self-created hotspot). This experiment runs entirely in STA mode.

This experiment extends the basic STA WiFi connection with AP scanning—before connecting, scan surrounding WiFi and print AP list (SSID, RSSI, channel), convenient for on-site debugging to confirm signal environment and channel usage. Basic WiFi STA connection flow (NVS init, event callback, EventGroup sync, etc.) is identical to 5.2 WIFI-STA Mode, only the new scan logic is shown here.

Basic review: 5. Network Protocols and Wireless Communication -> 2. WIFI-STA Mode -> WIFI-STA Mode

5.3 wifi_handler() — With AP Scan

This experiment's WiFi callback has one more function than basic STA: before connecting, scan surrounding WiFi, print AP list (SSID, signal strength RSSI, channel), then connect to target router. This is very useful when debugging WiFi connection issues on-site—you can see what hotspots are around, if the signal is strong enough, if there's co-channel interference.

Flow: STA starts → esp_wifi_scan_start() scan → SCAN_DONE event triggers → esp_wifi_scan_get_ap_records() get records → iterate and print → configure target AP → esp_wifi_connect().

xxxxxxxxxx
static void wifi_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
{
    if (base == WIFI_EVENT && id == WIFI_EVENT_STA_START) {
        esp_wifi_scan_start(NULL, false);
    }
    else if (base == WIFI_EVENT && id == WIFI_EVENT_SCAN_DONE) {
        uint16_t n = 0;
        esp_wifi_scan_get_ap_num(&n);
        if (n > 0) {
            uint16_t cnt = (n > MAX_SCAN) ? MAX_SCAN : n;
            wifi_ap_record_t *aps = calloc(cnt, sizeof(wifi_ap_record_t));
            esp_wifi_scan_get_ap_records(&cnt, aps);
            ESP_LOGI(TAG, "===== Found %d APs =====", cnt);
            for (int i = 0; i < cnt; i++) {
                ESP_LOGI(TAG, "[%2d] SSID: %-32s RSSI: %4d  CH: %d",
                         i + 1, aps[i].ssid, aps[i].rssi, aps[i].primary);
            }
            free(aps);
        }
        wifi_config_t cfg = {0};
        strcpy((char *)cfg.sta.ssid, WIFI_SSID);
        strcpy((char *)cfg.sta.password, WIFI_PASS);
        esp_wifi_set_config(WIFI_IF_STA, &cfg);
        esp_wifi_connect();
    }
    else if (base == WIFI_EVENT && id == WIFI_EVENT_STA_CONNECTED) {
    }
    else if (base == WIFI_EVENT && id == WIFI_EVENT_STA_DISCONNECTED) {
        wifi_event_sta_disconnected_t *e = data;
        ESP_LOGE(TAG, ">>> Disconnected! reason=%u  reconnecting... <<<", e->reason);
        esp_wifi_connect();
    }
    else if (base == IP_EVENT && id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t *e = data;
        ESP_LOGI(TAG, "Got IP: " IPSTR "  Gateway: " IPSTR,
                 IP2STR(&e->ip_info.ip), IP2STR(&e->ip_info.gw));
        xEventGroupSetBits(wifi_evt, WIFI_OK);
    }
}

5.4 socket_experiment() — Pack/Send/Receive/Verify

All new logic in this experiment is encapsulated in this function: create Socket → connect to server → pack → send → loop receive and verify. Receiving uses while(1) infinite loop, network debugging assistant sends one frame, ESP32 receives and parses immediately, sends ten frames and parses ten times, doesn't disconnect.

Key for packing: first memset to clear the entire struct—if not cleared, uninitialized fields may be random garbage data and checksum won't match. Then fill fields one by one, header fixed to 0xAA as frame header, type and length use htons() to convert to network byte order. Clear the checksum field itself first (calculation covers entire struct, doesn't include itself), then call calc_checksum() to calculate and fill back.

After receiving, unpack and verify (executed for each frame received in the loop): first save received checksum → clear checksum field in struct → recalculate checksum → compare with saved value. If same, data was not corrupted during transmission. Frames with header not equal to 0xAA and checksum mismatch are skipped by continue, continuing to wait for next frame.

xxxxxxxxxx
static void socket_experiment(void)
{
    /* 1. Create socket */
    int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock < 0) {
        ESP_LOGE(TAG, "1. socket() failed, errno=%d", errno);
        return;
    }
    ESP_LOGI(TAG, "1. socket() created OK (fd=%d)", sock);

    /* 2. Connect to server */
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port   = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &addr.sin_addr);

    ESP_LOGI(TAG, "2. connect() -> %s:%d ...", SERVER_IP, SERVER_PORT);
    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
        ESP_LOGE(TAG, "2. connect() failed! errno=%d", errno);
        close(sock);
        return;
    }
    ESP_LOGI(TAG, "2. connect() OK, TCP link established");

    /* ===== 3. Build packet ===== */
    proto_pkt_t pkt;
    memset(&pkt, 0, sizeof(pkt));

    pkt.header  = 0xAA;
    pkt.version = 1;
    pkt.type    = htons(0x0001);
    strcpy(pkt.data, "ESP32-TCP-PACKET");
    pkt.length  = htons((uint16_t)strlen(pkt.data));
    pkt.checksum = 0;
    pkt.checksum = calc_checksum((uint16_t *)&pkt, sizeof(pkt));

    ESP_LOGI(TAG, "3. Packet built OK!");
    ESP_LOGI(TAG, "   header   = 0x%02X", pkt.header);
    ESP_LOGI(TAG, "   version  = %u",  pkt.version);
    ESP_LOGI(TAG, "   type     = 0x%04X (net)", ntohs(pkt.type));
    ESP_LOGI(TAG, "   length   = %u (net)", ntohs(pkt.length));
    ESP_LOGI(TAG, "   data     = \"%s\"", pkt.data);
    ESP_LOGI(TAG, "   checksum = 0x%04X (net)", ntohs(pkt.checksum));
    ESP_LOGI(TAG, "   sizeof(pkt) = %u bytes", sizeof(pkt));

    /* ===== 4. Send packet ===== */
    int sent = send(sock, &pkt, sizeof(pkt), 0);
    if (sent > 0) {
        ESP_LOGI(TAG, "4. Send OK! sent %d/%u bytes", sent, sizeof(pkt));
    } else {
        ESP_LOGE(TAG, "4. Send failed! errno=%d", errno);
        close(sock);
        return;
    }



    /* ===== 5. Send/receive loop ===== */
    while (1) {
        proto_pkt_t rsp;
        memset(&rsp, 0, sizeof(rsp));

        // Receive 8-byte header
        int total = 0;
        while (total < 8) {
            int n = recv(sock, (uint8_t *)&rsp + total, 8 - total, 0);
            if (n <= 0) break;
            total += n;
        }
        if (total < 8) {
            vTaskDelay(pdMS_TO_TICKS(100));
            continue;
        }

        // Parse length, clamp to prevent overflow
        uint16_t body_len = ntohs(rsp.length);
        if (body_len > sizeof(rsp.data)) body_len = sizeof(rsp.data);
        int need = 8 + body_len;

        // Receive remaining payload
        while (total < need) {
            int n = recv(sock, (uint8_t *)&rsp + total, need - total, 0);
            if (n <= 0) break;
            total += n;
        }
        if (total < need) {
            vTaskDelay(pdMS_TO_TICKS(100));
            continue;
        }

        // Print packet fields
        ESP_LOGI(TAG, "5. Recv OK! %d bytes (need=%d)", total, need);
        ESP_LOGI(TAG, "   header   = 0x%02X", rsp.header);
        ESP_LOGI(TAG, "   version  = %u",  rsp.version);
        ESP_LOGI(TAG, "   type     = 0x%04X", ntohs(rsp.type));
        ESP_LOGI(TAG, "   length   = %u", body_len);
        ESP_LOGI(TAG, "   checksum = 0x%04X (rx)", ntohs(rsp.checksum));
        ESP_LOGI(TAG, "   data     = \"%.*s\"", (int)body_len, rsp.data);

        // Frame header check
        if (rsp.header != 0xAA) {
            ESP_LOGW(TAG, "   Bad header! expected 0xAA");
            ESP_LOG_BUFFER_HEX(TAG, &rsp, total);
            continue;
        }

        // Checksum verification
        uint16_t rx_cksum = rsp.checksum;
        rsp.checksum = 0;
        uint16_t calc = calc_checksum((uint16_t *)&rsp, need);
        if (rx_cksum == calc) {
            ESP_LOGI(TAG, "   Checksum OK! (0x%04X == 0x%04X)",
                     ntohs(rx_cksum), ntohs(calc));
        } else {
            ESP_LOGW(TAG, "   Checksum FAIL! rx=0x%04X calc=0x%04X",
                     ntohs(rx_cksum), ntohs(calc));
            ESP_LOG_BUFFER_HEX(TAG, &rsp, total);
        }
    }
}

### 5.5 app_main() — Entry Point

```c
void app_main(void)
{
    wifi_init();
    wifi_connect();
    socket_experiment();
}

5.6 Key Points of Pack Flow

xxxxxxxxxx
Clear struct → Fill header → Fill version → Fill type (htons) → Fill data
  → strlen calculate length → Fill length (htons) → checksum = 0 → calc_checksum(&pkt, 72)
  → Return value written back to pkt.checksum

6. Operation Flow

6.1 Build and Flash Flow

ESP-IDF Flash Flow: See ESP-IDF → 1. Before Development → 3. Build and Flash.

6.2 Network Debugging Assistant Configuration

This experiment requires a TCP server on PC to receive binary protocol packets from ESP32 and reply. Recommended to use NetAssist (Network Debugging Assistant), steps:

  1. Open NetAssist, protocol type on left select TCP Server

  2. Set port: local port fill 8080 (consistent with SERVER_PORT in code), click "Start Listening"

  3. Receive settings: Must check HEX Display (hexadecimal), otherwise binary frame header 0xAA, checksum and other fields will display as garbled text

  4. Send settings: Check HEX Send, reply data must also be in this protocol format (frame header AA + version 01 + type 00 01 + length + checksum + data), otherwise ESP32 checksum unpack will fail

  5. Confirm network: Ensure PC and ESP32 are connected to the same router (same subnet), and PC firewall allows port 8080 inbound

If NetAssist receives data but ESP32 logs show Recv timeout/no reply, the server is not replying. Check HEX send, reply in protocol format.

6.3 Pack Tool

Manual packing easily fills wrong length and checksum. Recommend using the accompanying pack_tool.py script:

  1. Open terminal, go to tools/ directory

  2. Run python pack_tool.py, enter interactive mode

  3.  

  1. Enter any text (Chinese or English), press Enter to output HEX protocol packet

  2. Copy output hex string, paste into network debugging assistant HEX send box

  3. Enter q to quit

xxxxxxxxxx
=======================================================
  Pack Tool
  Input text → Output HEX packet
  Input q to quit
=======================================================

>>> Hello YAHBOOM
  Frame len = 21B  |  length = 13  |  checksum = 0xF42A
  AA 01 00 01 00 0D F4 2A 48 65 6C 6C 6F 20 59 41 48 42 4F 4F 4D

>>> Hello ESP32
  Frame len = 19B  |  length = 11  |  checksum = 0x630A
  AA 01 00 01 00 0B 63 0A E4 BD A0 E5 A5 BD 45 53 50 33 32

>>> q
Bye.

Script Output

Core point: Frame length = 8 + length, automatically changes with input content, don't fixed fill 72 bytes or manually pad zeros. length and checksum are both automatically calculated by the script, no manual calculation needed.

6.4 Normal Log and Step Breakdown

After flashing, open serial monitor (baud rate 115200). Normal operation shows these stages in order:

Stage 1: Scan nearby APs and connect

xxxxxxxxxx
I (2962) SOCKET: ===== Found 10 APs =====
I (2962) SOCKET: [ 1] SSID: Yahboom2                         RSSI:  -20  CH: 6
I (2962) SOCKET: [ 2] SSID: Yahboom3                         RSSI:  -27  CH: 1
...

AP Scan Finding Surrounding WiFi

ESP32 starts STA and automatically scans nearby WiFi, printing each AP's SSID (name), RSSI (signal strength, closer to 0 is stronger), CH (channel). Then automatically connects to hotspot specified by WIFI_SSID.

Stage 2: Get IP address

After WiFi link is established, DHCP automatically assigns IP. Got IP shows local IP and gateway address. Network ready, can establish Socket connection.

xxxxxxxxxx
I (4362) esp_netif_handlers: sta ip: 192.168.11.181, mask: 255.255.255.0, gw: 192.168.11.1
I (4362) SOCKET: Got IP: 192.168.11.181  Gateway: 192.168.11.1
I (4362) SOCKET: WiFi connected successfully!

WiFi Connection Success

Stage 3: Establish TCP connection

xxxxxxxxxx
I (4162) SOCKET: 1. socket() created OK (fd=54)
I (4172) SOCKET: 2. connect() -> 192.168.11.183:8080 ...
I (4192) wifi:<ba-add>idx:1 (ifx:0, 80:8f:1d:20:9d:f7), tid:0, ssn:0, winSize:64
I (4202) SOCKET: 2. connect() OK, TCP link established

TCP Connection

Step 1 creates IPv4 TCP socket (returns fd=54), step 2 completes three-way handshake with server 192.168.11.183:8080.

Stage 4: Pack and send

In app_main(), ESP32 calls wifi_init() → wifi_connect() → socket_experiment() in sequence. socket_experiment() first completes Socket connection, then memset clears struct, fills fields, calculates checksum, finally send().

xxxxxxxxxx
I (3864) SOCKET: 3. Packet built OK!
I (3864) SOCKET:    header   = 0xAA
I (3865) SOCKET:    version  = 1
I (3865) SOCKET:    type     = 0x0001 (net)
I (3865) SOCKET:    length   = 16 (net)
I (3865) SOCKET:    data     = "ESP32-TCP-PACKET"
I (3866) SOCKET:    checksum = 0x8F3A (net)
I (3866) SOCKET:    sizeof(pkt) = 72 bytes
I (3868) SOCKET: 4. Send OK! sent 72/72 bytes

Pack and Send

Stage 5: Loop receive and unpack (network debugging assistant sends packet)

After ESP32 sends, enters while(1) loop to wait. Switch to pack_tool.py, input text to get HEX protocol packet, paste into NetAssist HEX send box and send.

xxxxxxxxxx
>>> Hello YAHBOOM
  Frame len = 21B  |  length = 13  |  checksum = 0xF42A
  AA 01 00 01 00 0D F4 2A 48 65 6C 6C 6F 20 59 41 48 42 4F 4F 4D

Paste the script's output hex string into NetAssist, check HEX send, click send:

ESP32 receives and parses each frame independently, serial output:

xxxxxxxxxx
I (14692) SOCKET: 5. Recv OK! 21 bytes (need=21)
I (14692) SOCKET:    header   = 0xAA
I (14692) SOCKET:    version  = 1
I (14702) SOCKET:    type     = 0x0001
I (14702) SOCKET:    length   = 13
I (14702) SOCKET:    checksum = 0x2AF4 (rx)
I (14702) SOCKET:    data     = "Hello YAHBOOM"
I (14712) SOCKET:    Checksum OK! (0x2AF4 == 0x2AF4)
<
TCP Server Experiment
HTTP Client Experiment

COMPANY

  • About us
  • Team Style
  • Blog

LEARNING & EXPLORATION

  • Tutorials
  • Edge AI hardware
  • Educational robots
  • Embedded intelligent hardware

WHERE TO BUY

  • Yahboom Online Store
  • Amazon
  • Alibaba
  • AliExpress
  • Mercado

TECHNOLOGY SUPPORT

  • Email: support@yahboom.com

BE A DISTRIBUTOR

  • Email: sales@yahboom.com
QR Code
QR Code

Copyright © 2017-2026 Yahboom. All Rights Reserved.

EN/中文