ESP32—CAM(Blinker) 与 STM32 串口通信(附详细代码)

项目实战背景-智能鱼箱设计

  • 远程控制:可将温度、浑浊度、液位等数据上传至手机APP,并可通过远程控制进行手动加热、加水和喂食操作。

最终实现效果展示,实现显示数据与按键控制并数据下发。

 1、硬件设计与软件搭建

硬件设计主要用到单片机串口3的资源

ESP32-CAM实物如图,最好带下载器下载更稳定。

ESP32-CAM与硬件连接需要引出ESP32排线与STM32连接

软件需要用到keil与 Visual Studio Code

 Visual Studio Code需安装platformio编译与烧录代码给单片机

详细的安装教程参考:
https://blog.csdn.net/qq_40018676/article/details/128680677

我是挂梯子在线下载的,不使用梯子时间可能花费比较长,请耐心等待~

2、STM32部分代码

        实现思路,定义帧头与帧尾,并定义需传输与接受的数据为结构体。并将需传输的数据统一格式uint8_t,这样每个字节的长度固定方便后续解析。

        当有效数据包被完整接收并校验通过后,直接将缓冲区数据按字节映射到全局控制结构体auto_control中(rx_buffer[1]对应cold_enablerx_buffer[2]对应heat_enable等,按固定偏移解析)。

下面为全部的代码,可删去不需要的结构体,并设置合理的Send_Data结构体(帧头+实际值+参数+控制位+帧尾)的方式。

1)单片机ESP32-CAM.h

#ifndef __ESP32_CAM_H
#define  __ESP32_CAM_H

#include <stdint.h>
#include "main.h"
#include "stm32f1xx_hal.h"
#include "ESP32_CAM.h"
#include <string.h>
#include "KeyMode.h"
#include "Show.h"

#define SEND_DATA_SIZE    13


#define FRAME_HEADER      0X7B //Frame_header //帧头
#define FRAME_TAIL        0X7D //Frame_tail   //帧尾

typedef struct {
    uint8_t temperature;  // 单位:℃
    uint8_t turbidity;    // 单位:NTU
    uint8_t level;        // 单位:cm
} show_data;

typedef struct _SEND_DATA_  
{
	unsigned char buffer[SEND_DATA_SIZE];
	struct _Sensor_Str_
	{
		unsigned char Frame_Header; //1个字节
		show_data usart_Data;		//传输实际数值
		SensorParameters params;	//传输参数
		ControlFlags flags;			//传输结构体
		unsigned char Frame_Tail;   //1 bytes 
	}Sensor_Str;
}SEND_DATA;

typedef struct __attribute__((packed)) {
    uint8_t header;          // 0x7B
    uint8_t cold_enable;     // 制冷
    uint8_t heat_enable;     // 加热
    uint8_t water_add;       // 加水
    uint8_t pump_active;     // 水泵
    uint8_t feed_trigger;    // 喂食
    uint8_t checksum;        // 校验和
    uint8_t footer;          // 0x7D
} ControlPacket;


extern SensorParameters sensor_params;
extern ControlFlags control;
extern float DS18B20_ShowData;
extern float VoltageValue[3];

// 函数声明
void StartTask04_ESP32_CAM(void);
void SendSensorData(void);
void data_transition(void);

2)单片机ESP32-CAM.c

(代码不够简洁,请见谅),主要就是收发数据与数据处理。

#include "main.h"
#include "stm32f1xx_hal.h"
#include "ESP32_CAM.h"
#include <string.h>
#include "KeyMode.h"
#include "Show.h"
#include <string.h> 
SEND_DATA Send_Data;

extern UART_HandleTypeDef huart3;
extern AUTO_CONTROL auto_control;
volatile ControlPacket receivedPacket;
volatile uint8_t packetReady = 0;

void usart3_send(uint8_t data)
{
	USART3->DR = data;
	while((USART3->SR&0x40)==0);	
}

void StartTask04_ESP32_CAM(void)
{
		data_transition(); 
		SendSensorData();     //Serial port 3 (ROS) sends data  //串口3发送数据
}

uint8_t rx_buffer[sizeof(ControlPacket)]; // 全局接收缓冲区
volatile uint8_t packetReady_2 = 0;        // 数据包就绪标志

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 
{
    static uint8_t rx_index = 0;  // 当前接收位置
    static uint32_t last_rx = 0;  // 最后接收时间戳
    
    if(huart->Instance == USART3) 
    {
        // 超时重置(300ms无新数据则清空缓冲区)
        if(HAL_GetTick() - last_rx > 300) {
            rx_index = 0;
        }
        last_rx = HAL_GetTick();
        
        // 写入缓冲区
        if(rx_index < sizeof(ControlPacket)) 
        {
            rx_buffer[rx_index++] = huart->Instance->DR;
            
            // 帧头即时校验(首字节必须为0x7B)
            if(rx_index == 1 && rx_buffer[0] != 0x7B) {
                rx_index = 0; // 帧头错误则重置
            }
            
            // 完整包接收完成
            if(rx_index == sizeof(ControlPacket)) 
            {
                // 基础校验:帧头0x7B + 帧尾0x7D
                if(rx_buffer[0] == 0x7B && rx_buffer[sizeof(ControlPacket)-1] == 0x7D) 
                {
                    // 直接操作全局结构体(无需临时变量)
						auto_control.cold_enable  =  rx_buffer[1];
						auto_control.heat_enable  =  rx_buffer[2];
						auto_control.water_add  =  rx_buffer[3];
						auto_control.pump_active  =  rx_buffer[4];
						auto_control.feed_trigger =  rx_buffer[5];
                    
                    packetReady_2 = 1; // 设置就绪标志
                }
                rx_index = 0; // 无论校验是否通过都重置
            }
        }
        
        // 继续接收下一字节
        HAL_UART_Receive_IT(huart, &rx_buffer[rx_index], 1);
    }
}

void SendSensorData(void)
{

	unsigned char i = 0;	
	for(i=0; i<13; i++)
	{
		usart3_send(Send_Data.buffer[i]);
	}	

}

void data_transition(void)
{
	Send_Data.Sensor_Str.Frame_Header = FRAME_HEADER; //Frame_header //帧头
	Send_Data.Sensor_Str.Frame_Tail = FRAME_TAIL;     //Frame_tail //帧尾
	//实际数值
	Send_Data.Sensor_Str.usart_Data.temperature = (uint8_t)DS18B20_ShowData;
	Send_Data.Sensor_Str.usart_Data.turbidity =  (uint8_t)VoltageValue[0];
	Send_Data.Sensor_Str.usart_Data.level = (uint8_t)VoltageValue[1];
	
	//参数
	Send_Data.Sensor_Str.params.temperature = (uint8_t)sensor_params.temperature;
	Send_Data.Sensor_Str.params.tds_value = (uint8_t)sensor_params.tds_value;
	Send_Data.Sensor_Str.params.height = (uint8_t)sensor_params.height;
	
	//标志位
	Send_Data.Sensor_Str.flags.cold_enable = control.cold_enable;
	Send_Data.Sensor_Str.flags.heat_enable = control.heat_enable;
	Send_Data.Sensor_Str.flags.water_add = control.water_add;
	Send_Data.Sensor_Str.flags.pump_active = control.pump_active;
	Send_Data.Sensor_Str.flags.feed_trigger = control.feed_trigger;
	
	
	//传输buffer
	Send_Data.buffer[0] = Send_Data.Sensor_Str.Frame_Header;
	Send_Data.buffer[1] = Send_Data.Sensor_Str.usart_Data.temperature;
	Send_Data.buffer[2] = Send_Data.Sensor_Str.usart_Data.turbidity;
	Send_Data.buffer[3] = Send_Data.Sensor_Str.usart_Data.level;
	Send_Data.buffer[4] = Send_Data.Sensor_Str.params.temperature;
	Send_Data.buffer[5] = Send_Data.Sensor_Str.params.tds_value;
	Send_Data.buffer[6] = Send_Data.Sensor_Str.params.height;
	Send_Data.buffer[7] = Send_Data.Sensor_Str.flags.cold_enable;
	Send_Data.buffer[8] = Send_Data.Sensor_Str.flags.heat_enable;
	Send_Data.buffer[9] = Send_Data.Sensor_Str.flags.water_add;
	Send_Data.buffer[10] = Send_Data.Sensor_Str.flags.pump_active;
	Send_Data.buffer[11] = Send_Data.Sensor_Str.flags.feed_trigger;
	Send_Data.buffer[12] = Send_Data.Sensor_Str.Frame_Tail;
}

如何验证STM32发送是否正确呢?

使用串口助手,看是否是满足自己定义的数据

3、ESP32-CAM部分代码

附上参考资料:

ESP32-CAM摄像头开发板上手体验 - 《ESP项目学习分享》 - 极客文档

【VScode技巧】:platformio部署ESP32Cam开发板_platform 使用esp32cam-CSDN博客

点灯科技

我在使用ESP32-CAM主要使用的还是去物联网功能,对摄像头功能并未使用(也可自行添加,代码详见上述资料)

按照参考资料2操作后还需下载Blinker库

并引用到include中

在main函数中编写所需的物联网核心代码

主要实现功能

  • 通过硬件串口 2 连接 STM32,定义 14、15 引脚为收发引脚。使用 Blinker 库实现物联网功能,配置 WiFi 和设备密钥连接平台。
  • 定义了 5 个控制按钮及回调函数,按下时切换对应设备(制冷、加热等)的开关状态并标记发送。自定义 SensorPacket 结构体与 STM32 保持通信协议一致,包含帧头、传感器数据、控制标志等。
  • 接收 STM32 数据时通过帧头 0x7B 和帧尾 0x7D 校验,超时 50ms 重置接收。收到有效数据后更新 Blinker 平台显示。发送数据时生成含校验和的 8 字节数据包,包含各设备控制状态。

下面是对几个重点函数的解析最后给出全部代码

1)对显示组件

  1. 定义 6 个 BlinkerNumber 组件(Temp、TDS 等)分别关联对应标识符。
  2. 接收 STM32 数据时,在 SerialSTM32.available () 循环中解析 SensorPacket 结构体,提取温度、浊度等数据。
  3. 调用 Temp.print ()、TDS.print () 等函数,将解析出的传感器数据发送到 Blinker 平台显示。
  4. 设备状态通过 Button1.print () 等函数更新,根据 cooling_flag 等标志位,调用 Button1.text () 设置显示文本(START/STOP),并通过 Button1.print () 推送至平台。
  5. 每次 loop () 中会刷新按钮状态,确保显示与实际控制标志同步。

显示组件定义

// 自定义Blinker显示组件,对应APP中的数据展示控件
// 标识符需与Blinker APP中配置的控件标识符一致
BlinkerNumber Temp(const_cast<char*>("temp"));      // 温度显示组件(标识符"temp")
BlinkerNumber TDS(const_cast<char*>("tds"));        // 浊度显示组件(标识符"tds")
BlinkerNumber Height(const_cast<char*>("height"));  // 液位高度显示组件(标识符"height")

BlinkerNumber P_Temp(const_cast<char*>("P_temp"));  // 温度参数显示组件(标识符"P_temp")
BlinkerNumber P_TDS(const_cast<char*>("P_tds"));    // 浊度参数显示组件(标识符"P_tds")
BlinkerNumber P_Height(const_cast<char*>("P_height")); // 液位参数显示组件(标识符"P_height")

// 控制按钮组件(同时用于显示设备状态)
BlinkerButton Button1(const_cast<const char*>(BUTTON_1)); // 制冷按钮(标识符"btn-1")
BlinkerButton Button2(const_cast<const char*>(BUTTON_2)); // 加热按钮(标识符"btn-2")
BlinkerButton Button3(const_cast<const char*>(BUTTON_3)); // 加水按钮(标识符"btn-3")
BlinkerButton Button4(const_cast<const char*>(BUTTON_4)); // 水泵按钮(标识符"btn-4")
BlinkerButton Button5(const_cast<const char*>(BUTTON_5)); // 喂食按钮(标识符"btn-5")

数据接收与解析(loop 函数中)

见下方详细代码

2)对按键组件

  • 回调函数是按键与逻辑的工具,当 APP 中按下对应按键时,Blinker 库会自动调用绑定的回调函数。
  • state == BLINKER_CMD_BUTTON_PRESSED用于判断按键为 “按下” 状态(避免重复触发)。
  • 核心逻辑是通过!运算符切换状态标志(如cooling_flag),并设置sendFlag = 1,通知主循环将新状态发送给 STM32。
  • attach方法用于将按键组件与回调函数关联,确保按键被按下时能触发对应的逻辑。

按键组件定义

// 定义按键标识符(需与Blinker APP中控件的标识符完全一致)
#define BUTTON_1 "btn-1"  // 制冷按键标识符
#define BUTTON_2 "btn-2"  // 加热按键标识符
#define BUTTON_3 "btn-3"  // 加水按键标识符
#define BUTTON_4 "btn-4"  // 水泵按键标识符
#define BUTTON_5 "btn-5"  // 喂食按键标识符

// 定义Blinker按键组件,关联标识符与实际功能
BlinkerButton Button1(const_cast<const char*>(BUTTON_1)); // 制冷控制按键
BlinkerButton Button2(const_cast<const char*>(BUTTON_2)); // 加热控制按键
BlinkerButton Button3(const_cast<const char*>(BUTTON_3)); // 加水控制按键
BlinkerButton Button4(const_cast<const char*>(BUTTON_4)); // 水泵控制按键
BlinkerButton Button5(const_cast<const char*>(BUTTON_5)); // 喂食控制按键

// 按键控制标志(0:关闭,1:开启),用于记录按键状态
uint8_t cooling_flag = 0;    // 制冷状态标志
uint8_t heating_flag = 0;    // 加热状态标志
uint8_t water_add_flag = 0;  // 加水状态标志
uint8_t pump_flag = 0;       // 水泵状态标志
uint8_t feed_flag = 0;       // 喂食状态标志

按键回调函数(响应按键按下事件)

// 制冷按键回调函数(APP中按下"btn-1"时触发)
void button1_callback(const String & state) {
    if (state == BLINKER_CMD_BUTTON_PRESSED) {  // 判断按键状态为"按下"
        cooling_flag = !cooling_flag;  // 切换制冷状态(0→1或1→0)
        sendFlag = 1;  // 置位发送标志,通知STM32状态变化
        Serial.printf("[DEBUG] Button1 pressed! sendFlag=%d\n", sendFlag);  // 调试信息
    }
}

void setup() 
{
    // 其他初始化(串口、WiFi等)...
    
    // 绑定按键与回调函数:将按键组件与对应的逻辑处理函数关联
    Button1.attach(button1_callback);  // 制冷按键绑定回调
    Button2.attach(button2_callback);  // 加热按键绑定回调
    Button3.attach(button3_callback);  // 加水按键绑定回调
    Button4.attach(button4_callback);  // 水泵按键绑定回调
    Button5.attach(button5_callback);  // 喂食按键绑定回调
}

3)ESP32端代码

        以下为全部ESP32端全部代码,对auth[](设备密钥)、 ssid[](WIFI名称可设自己的手机热点)和 pswd[](WIFI密码)自己按要求设置。设备密钥在Blinker手机端查看。

#define BLINKER_WIFI
#include <Blinker.h>
#include <HardwareSerial.h>
#include "ESP32_CAM_SERVER.h" 

// 硬件串口配置
#define STM32_RX_PIN 14  // ESP32的RX接STM32的TX
#define STM32_TX_PIN 15  // ESP32的TX接STM32的RX
HardwareSerial SerialSTM32(2);
#define BUTTON_1 "btn-1"
#define BUTTON_2 "btn-2"
#define BUTTON_3 "btn-3"
#define BUTTON_4 "btn-4"
#define BUTTON_5 "btn-5"

void button1_callback(const String & state);
void button2_callback(const String & state);
void button3_callback(const String & state);
void button4_callback(const String & state);
void button5_callback(const String & state);
// Blinker配置
#define TEXTE_1 "sensor"
char auth[] = "xxx";
char ssid[] = "xxx";
char pswd[] = "xxx";

bool setup_camera = false;
uint8_t sendFlag = 0;
// 自定义组件
BlinkerNumber Temp(const_cast<char*>("temp"));
BlinkerNumber TDS(const_cast<char*>("tds"));
BlinkerNumber Height(const_cast<char*>("height"));

BlinkerNumber P_Temp(const_cast<char*>("P_temp"));
BlinkerNumber P_TDS(const_cast<char*>("P_tds"));
BlinkerNumber P_Height(const_cast<char*>("P_height"));

BlinkerButton Button1(const_cast<const char*>(BUTTON_1)); // 设备控制按钮-制冷
BlinkerButton Button2(const_cast<const char*>(BUTTON_2)); // 设备控制按钮-加热
BlinkerButton Button3(const_cast<const char*>(BUTTON_3)); // 设备控制按钮-加水
BlinkerButton Button4(const_cast<const char*>(BUTTON_4)); // 设备控制按钮-泵
BlinkerButton Button5(const_cast<const char*>(BUTTON_5)); // 设备控制按钮-喂食
// 按键控制标志(0:关闭,1:开启)
uint8_t cooling_flag = 0;    // 制冷
uint8_t heating_flag = 0;   // 加热
uint8_t water_add_flag = 0;  // 加水
uint8_t pump_flag = 0;       // 泵
uint8_t feed_flag = 0;       // 喂食

// BlinkerText Text1(const_cast<char*>(TEXTE_1));  
// 协议定义(必须与STM32完全一致)
// 在ESP32端定义相同协议结构体
#pragma pack(push, 1)  // 紧凑对齐

typedef struct {
    uint8_t frame_header;       // 帧头
    uint8_t temperature;        // 实际温度(STM32发送时转换为uint8_t)
    uint8_t tds_value;           // 浊度
    uint8_t height;              // 高度

    uint8_t temp;  // 参数
    uint8_t turbidity;
    uint8_t level;

    uint8_t cold_enable;  // 位域定义(与STM32位域顺序一致)
    uint8_t heat_enable;
    uint8_t water_add;
    uint8_t pump_active;
    uint8_t feed_trigger;

    uint8_t frame_tail;          // 帧尾
} SensorPacket;

#pragma pack(pop)  // 恢复默认对齐


// 全局变量
uint8_t rx_buffer[sizeof(SensorPacket)];  // 接收缓冲区
uint8_t rx_index = 0;                     // 缓冲区索引
bool packet_started = false;               // 是否开始接收数据包
unsigned long last_rx_time = 0;            // 最后接收时间(用于超时检测)


#define FRAME_HEADER 0x7B  // 帧头 '{'
#define FRAME_TAIL   0x7D  // 帧尾 '}'
#define SEND_SIZE 8     // 发送数据包大小改为8字节
uint8_t sendBuffer[SEND_SIZE];             // 发送缓冲区
uint8_t Start_flag = 0;                   // 启停状态
unsigned long BlinkerTime = 0; 
// 在现有函数声明区域添加
uint8_t calculateChecksum(uint8_t *buffer, int length) 
{
    uint8_t checksum = 0;
    for(int i = 0; i < length; i++) 
    {
        checksum ^= buffer[i];
    }
    return checksum;
}


#define BLINKER_PRINT_DEBUG // 启用Blinker内部调试日志
#define BLINKER_PRINT Serial // 指定调试输出到Serial


// 制冷按键回调
void button1_callback(const String & state) {
    if (state == BLINKER_CMD_BUTTON_PRESSED) {
        cooling_flag = !cooling_flag;
        sendFlag = 1;
        Serial.printf("[DEBUG] Button1 pressed! sendFlag=%d\n", sendFlag);
    }
}

// 加热按键回调
void button2_callback(const String & state) {
    if (state == BLINKER_CMD_BUTTON_PRESSED) {
        heating_flag = !heating_flag;
        sendFlag = 1;
        Serial.printf("[DEBUG] Button2 pressed! sendFlag=%d\n", sendFlag);
    }
}

// 加水按键回调
void button3_callback(const String & state) {
    if (state == BLINKER_CMD_BUTTON_PRESSED) {
        water_add_flag = !water_add_flag;
        sendFlag = 1;
        Serial.printf("[DEBUG] Button3 pressed! sendFlag=%d\n", sendFlag);
    }
}

// 泵控制回调
void button4_callback(const String & state) {
    if (state == BLINKER_CMD_BUTTON_PRESSED) {
        pump_flag = !pump_flag;
        sendFlag = 1;
        Serial.printf("[DEBUG] Button4 pressed! sendFlag=%d\n", sendFlag);
    }
}

// 喂食按键回调
void button5_callback(const String & state) {
    if (state == BLINKER_CMD_BUTTON_PRESSED) {
        feed_flag = !feed_flag;
        sendFlag = 1;
        Serial.printf("[DEBUG] Button5 pressed! sendFlag=%d\n", sendFlag);
    }
}

// 数据发送函数
void sendDataToSTM32() 
{
    sendBuffer[0] = FRAME_HEADER;
    sendBuffer[1] = cooling_flag; 
    sendBuffer[2] = heating_flag;   
    sendBuffer[3] = water_add_flag;   
    sendBuffer[4] = pump_flag;  
    sendBuffer[5] = feed_flag;   
    sendBuffer[6] = calculateChecksum(sendBuffer, 6);  // 校验和
    sendBuffer[7] = FRAME_TAIL;

    SerialSTM32.write(sendBuffer, SEND_SIZE);  // 发送数据

    Serial.println("==================================\n");
      for(int i=0; i<=7; i++)
    {
         Serial.printf("sendBuffer: %02X ", sendBuffer[i]);
         SerialSTM32.printf("%02X ", sendBuffer[i]);
    }
   Serial.println("==================================\n");
}

void setup() 
{
    Serial.begin(115200);
    BLINKER_DEBUG.stream(Serial);
    SerialSTM32.begin(115200, SERIAL_8N1, STM32_RX_PIN, STM32_TX_PIN);
    
    Blinker.begin(auth, ssid, pswd);
    // Blinker.attachData(dataRead);

    Button1.attach(button1_callback);  // 制冷
    Button2.attach(button2_callback);  // 加热
    Button3.attach(button3_callback);  // 加水
    Button4.attach(button4_callback);  // 泵
    Button5.attach(button5_callback);  // 喂食
}


void loop() 
{
    Blinker.run();
    
     // 初始化摄像头(仅一次)
     if (Blinker.connected() && !setup_camera)
     {
         setupCamera();
         setup_camera = true;
 
         Blinker.printObject("video", "{\"str\":\"mjpg\",\"url\":\"http://"+ WiFi.localIP().toString() + "\"}");
     }


    // 发送控制指令
    if(sendFlag == 1) 
    {
        sendDataToSTM32();
        sendFlag = 0;  // 重置发送标志
    }


    // 接收数据包
    while(SerialSTM32.available()) 
    {
        uint8_t c = SerialSTM32.read();

        if(millis() - last_rx_time > 50)
        {
            rx_index = 0;
            packet_started = false;
        }
        last_rx_time = millis();

        if(c == 0x7B) // 帧头
        {
            packet_started = true;
            rx_index = 0;                  // 重置索引
            rx_buffer[rx_index++] = c;     // 存储帧头
            continue;   
        }
       
        if(packet_started)
        {
            rx_buffer[rx_index++] = c;
            if(rx_index >= sizeof(SensorPacket))
            {
                packet_started = false;
                SensorPacket *packet = (SensorPacket *)rx_buffer;
                if(packet->frame_tail == 0x7D) // 帧尾
                {
                    // 处理数据包
                    Temp.print(packet->temperature);
                    TDS.print(packet->tds_value);
                    Height.print(packet->height); 
                 }          
                cooling_flag = packet->cold_enable; // 更新控制标志
                heating_flag = packet->heat_enable; // 更新控制标志
                water_add_flag = packet->water_add; // 更新控制标志     
                pump_flag = packet->pump_active; // 更新控制标志
                feed_flag = packet->feed_trigger; // 更新控制标志
                
                Button1.print(cooling_flag ? "ON" : "OFF");
                Button2.print(heating_flag ? "ON" : "OFF");
                Button3.print(water_add_flag ? "ON" : "OFF");
                Button4.print(pump_flag ? "ON" : "OFF");
                Button5.print(feed_flag ? "ON" : "OFF");
                rx_index = 0; // 重置索引
            }
        }

    }
        // Button1状态更新(制冷)
        if(cooling_flag) {
            Button1.icon("icon_1");
            Button1.color("#FFFFFF");
            Button1.text("START");
        } else {
            Button1.icon("icon_1");
            Button1.color("#FFFFFF");
            Button1.text("STOP");
        }
        Button1.print();
    
        // Button2状态更新(加热)
        if(heating_flag) {
            Button2.icon("icon_1");
            Button2.color("#FFFFFF");
            Button2.text("START");
        } else {
            Button2.icon("icon_1");
            Button2.color("#FFFFFF");
            Button2.text("STOP");
        }
        Button2.print();
    
        // Button3状态更新(加水)
        if(water_add_flag) {
            Button3.icon("icon_1");
            Button3.color("#FFFFFF");
            Button3.text("START");
        } else {
            Button3.icon("icon_1");
            Button3.color("#FFFFFF");
            Button3.text("STOP");
        }
        Button3.print();
    
        // Button4状态更新(泵)
        if(pump_flag) {
            Button4.icon("icon_1");
            Button4.color("#FFFFFF");
            Button4.text("START");
        } else {
            Button4.icon("icon_1");
            Button4.color("#FFFFFF");
            Button4.text("STOP");
        }
        Button4.print();
    
        // Button5状态更新(喂食)
        if(feed_flag) {
            Button5.icon("icon_1");
            Button5.color("#FFFFFF");
            Button5.text("START");
        } else {
            Button5.icon("icon_1");
            Button5.color("#FFFFFF");
            Button5.text("STOP");
        }
        Button5.print();

}

4、现象与总结

        单片机部分最重要是定义帧格式,设置发送数据给串口看帧是否准确,ESP32端若只是显示数据那么不是很难只需要接受并解析数据,若是要按键回调数据稍复杂一些需要定义回调函数与绑定链接,Blinker也需要设置合适的组件,同时Blinker按键按下单片机需要有一个响应延迟最好按住按键不松手。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值