You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32通过SPI接收数据发送至Android时的数据不匹配问题求助

SPI数据索引不匹配问题排查与修复

问题背景

通过ESP32的SPI从机模式获取数据,再通过WiFi发送到Android设备,但WiFi发送的耗时导致SPI数据出现数组索引不匹配的情况。以下是当前使用的ESP32代码:

/*********************************************************************************/
#include <ESP32SPISlave.h>
#include <WiFi.h>

ESP32SPISlave slave;

#define StatusLED 27
const char* ssid     = "YOUR_ID";
const char* password = "YOUR_PASSWORD";

WiFiServer server(80);

IPAddress local_IP(192,168,3,14);
IPAddress gateway(192,168,3,5);
IPAddress subnet(255,255,255,0);

static constexpr uint32_t BUFFER_SIZE {10};
uint8_t spi_slave_tx_buf[BUFFER_SIZE];
uint8_t spi_slave_rx_buf[BUFFER_SIZE];
uint8_t spi_slave_rx_Localbuf[BUFFER_SIZE];
uint32_t Client_buf[4];
bool WIFI_Flag;

byte count = 0;

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

    // LED init
    pinMode(StatusLED, OUTPUT);

    // HSPI = CS: 15, CLK: 14, MOSI: 13, MISO: 12 -> default
    // VSPI = CS: 5, CLK: 18, MOSI: 23, MISO: 19
    slave.setDataMode(SPI_MODE0);
    slave.begin();

    // clear buffers
    memset(spi_slave_tx_buf, 0, BUFFER_SIZE);
    memset(spi_slave_rx_buf, 0, BUFFER_SIZE);

    // We start by connecting to a WiFi network

  Serial.print("Setting AP (Access Point)…");
  Serial.println(WiFi.softAPConfig(local_IP,gateway,subnet)?"Ready" : "Failed!");

  //*server = start_webserver();
  Serial.print("Starting Access Point)…");
  Serial.println( WiFi.softAP(ssid, password)?"Ready" : "Failed!");
  
  Serial.print("AP IP address: ");
  Serial.println(WiFi.softAPIP());
  server.begin();

}

void loop() {
    // Switch OFF LED
    // Switch ON LED
    digitalWrite(StatusLED, HIGH);  // turn the LED on (HIGH is the voltage level)
    
    
    if (slave.remained() == 0) {
        slave.queue(spi_slave_rx_buf, spi_slave_tx_buf, BUFFER_SIZE);
    }  

    // if transaction has completed from master,
    // available() returns size of results of transaction,
    // and buffer is automatically updated

    if (slave.available()) {

        // Switch ON LED
        digitalWrite(StatusLED, LOW);  // turn the LED off (LOW is the voltage level)  
        

        if(count != 0)
        {
          switch(count)
                {
                  case 1: {Client_buf[0]   =  spi_slave_rx_buf[0]; break;}
                  case 2: {Client_buf[0]  |=  spi_slave_rx_buf[0] << 8; break;}
                  case 3: {Client_buf[1]   =  spi_slave_rx_buf[0]; break;}
                  case 4: {Client_buf[1]  |=  spi_slave_rx_buf[0] << 8; break;}
                  case 5: {Client_buf[2]   =  spi_slave_rx_buf[0]; break;}
                  case 6: {Client_buf[2]  |=  spi_slave_rx_buf[0]<<8;  break;}
                  case 7: {Client_buf[3]   =  spi_slave_rx_buf[0]; break;}
                  case 8: {Client_buf[3]  |=  spi_slave_rx_buf[0]<<8; WIFI_Flag = 1; break;}
                }
        }
       count++;
        
        slave.pop();
    }

        if(WIFI_Flag)
        {
          WiFiClient client = server.available();     
          
          client.println("HTTP/1.1 200 OK");
          client.println("Content-type:text/html");
          client.println("");
                   
            client.print(Client_buf[0]); 
            client.print(","); 
            client.print(Client_buf[1]); 
            client.print(","); 
            client.print(Client_buf[2]); 
            client.print(","); 
            client.print(Client_buf[3]); 
          WIFI_Flag = 0;
          count = 0;
        }        
}
/*********************************************************************************/

问题根源与修复方案

核心问题

  1. SPI数据被覆盖:WiFi发送耗时阻塞主循环,导致新的SPI传输直接覆盖spi_slave_rx_buf中未处理的数据,引发索引错位。
  2. 计数逻辑混乱:count变量在SPI事务和WiFi发送两个流程中被修改,多任务环境下容易出现计数错误。
  3. WiFi发送阻塞主循环:直接在主循环中处理WiFi发送,占用大量时间,导致SPI数据处理不及时。

修复步骤

1. 新增缓存区存储SPI数据

在SPI事务完成后立即将数据存入独立缓存,避免被后续传输覆盖:

// 全局变量新增
uint8_t spi_rx_cache[8]; // 对应8次SPI传输的单字节数据
byte cache_index = 0;

在SPI数据处理块中修改为:

if (slave.available()) {
    digitalWrite(StatusLED, LOW);  
    // 将当前SPI数据存入缓存
    if(cache_index < 8){
        spi_rx_cache[cache_index] = spi_slave_rx_buf[0];
        cache_index++;
    }
    // 缓存满8个字节时组装完整数据
    if(cache_index == 8){
        uint32_t temp_buf[4];
        temp_buf[0] = spi_rx_cache[0] | (spi_rx_cache[1] << 8);
        temp_buf[1] = spi_rx_cache[2] | (spi_rx_cache[3] << 8);
        temp_buf[2] = spi_rx_cache[4] | (spi_rx_cache[5] << 8);
        temp_buf[3] = spi_rx_cache[6] | (spi_rx_cache[7] << 8);
        // 后续将temp_buf发送到WiFi任务
        cache_index = 0;
    }
    slave.pop();
    digitalWrite(StatusLED, HIGH);
}

2. 创建独立WiFi发送任务

使用FreeRTOS创建单独的WiFi发送任务,避免阻塞主循环的SPI处理:

// 全局变量新增
QueueHandle_t data_queue;

// WiFi发送任务函数
void wifi_send_task(void *pvParameters){
    WiFiClient client;
    uint32_t send_buf[4];
    while(1){
        // 等待队列中的数据
        if(xQueueReceive(data_queue, send_buf, portMAX_DELAY)){
            client = server.available();
            if(client && client.connected()){
                client.println("HTTP/1.1 200 OK");
                client.println("Content-type:text/html");
                client.println("");
                client.print(send_buf[0]); 
                client.print(","); 
                client.print(send_buf[1]); 
                client.print(","); 
                client.print(send_buf[2]); 
                client.print(","); 
                client.print(send_buf[3]); 
                client.flush();
                client.stop();
            }
        }
    }
}

// 在setup()中初始化队列和任务
void setup(){
    // ... 原有代码 ...
    data_queue = xQueueCreate(5, sizeof(uint32_t)*4); // 最多缓存5组数据
    xTaskCreate(wifi_send_task, "WiFiSend", 2048, NULL, 1, NULL);
}

然后在缓存满时将数据发送到队列:

if(cache_index == 8){
    uint32_t temp_buf[4];
    temp_buf[0] = spi_rx_cache[0] | (spi_rx_cache[1] << 8);
    temp_buf[1] = spi_rx_cache[2] | (spi_rx_cache[3] << 8);
    temp_buf[2] = spi_rx_cache[4] | (spi_rx_cache[5] << 8);
    temp_buf[3] = spi_rx_cache[6] | (spi_rx_cache[7] << 8);
    xQueueSend(data_queue, temp_buf, 0); // 非阻塞发送
    cache_index = 0;
}

3. 移除冗余计数变量

删除原有的count和WIFI_Flag变量,改用cache_index追踪缓存进度,简化逻辑。

完整修复后代码

#include <ESP32SPISlave.h>
#include <WiFi.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>

ESP32SPISlave slave;

#define StatusLED 27
const char* ssid     = "YOUR_ID";
const char* password = "YOUR_PASSWORD";

WiFiServer server(80);

IPAddress local_IP(192,168,3,14);
IPAddress gateway(192,168,3,5);
IPAddress subnet(255,255,255,0);

static constexpr uint32_t BUFFER_SIZE {10};
uint8_t spi_slave_tx_buf[BUFFER_SIZE];
uint8_t spi_slave_rx_buf[BUFFER_SIZE];
uint8_t spi_rx_cache[8];
QueueHandle_t data_queue;

byte cache_index = 0;

void wifi_send_task(void *pvParameters){
    WiFiClient client;
    uint32_t send_buf[4];
    while(1){
        if(xQueueReceive(data_queue, send_buf, portMAX_DELAY)){
            client = server.available();
            if(client && client.connected()){
                client.println("HTTP/1.1 200 OK");
                client.println("Content-type:text/html");
                client.println("");
                client.print(send_buf[0]); 
                client.print(","); 
                client.print(send_buf[1]); 
                client.print(","); 
                client.print(send_buf[2]); 
                client.print(","); 
                client.print(send_buf[3]); 
                client.flush();
                client.stop();
            }
        }
    }
}

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

    pinMode(StatusLED, OUTPUT);

    slave.setDataMode(SPI_MODE0);
    slave.begin();

    memset(spi_slave_tx_buf, 0, BUFFER_SIZE);
    memset(spi_slave_rx_buf, 0, BUFFER_SIZE);
    memset(spi_rx_cache, 0, sizeof(spi_rx_cache));

    Serial.print("Setting AP (Access Point)…");
    Serial.println(WiFi.softAPConfig(local_IP,gateway,subnet)?"Ready" : "Failed!");

    Serial.print("Starting Access Point)…");
    Serial.println( WiFi.softAP(ssid, password)?"Ready" : "Failed!");
    
    Serial.print("AP IP address: ");
    Serial.println(WiFi.softAPIP());
    server.begin();

    data_queue = xQueueCreate(5, sizeof(uint32_t)*4);
    xTaskCreate(wifi_send_task, "WiFiSend", 2048, NULL, 1, NULL);
}

void loop() {
    digitalWrite(StatusLED, HIGH);

    // 确保SPI队列始终就绪,不会错过主设备传输
    if (slave.remained() == 0) {
        slave.queue(spi_slave_rx_buf, spi_slave_tx_buf, BUFFER_SIZE);
    }  

    if (slave.available()) {
        digitalWrite(StatusLED, LOW);  

        if(cache_index < 8){
            spi_rx_cache[cache_index] = spi_slave_rx_buf[0];
            cache_index++;
        }

        if(cache_index == 8){
            uint32_t temp_buf[4];
            temp_buf[0] = spi_rx_cache[0] | (spi_rx_cache[1] << 8);
            temp_buf[1] = spi_rx_cache[2] | (spi_rx_cache[3] << 8);
            temp_buf[2] = spi_rx_cache[4] | (spi_rx_cache[5] << 8);
            temp_buf[3] = spi_rx_cache[6] | (spi_rx_cache[7] << 8);
            xQueueSend(data_queue, temp_buf, 0);
            cache_index = 0;
        }
        
        slave.pop();
    }
}

内容的提问来源于stack exchange,提问作者Venugopal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 15:55:09