以下是一个简单的示例,演示了如何使用AT指令从单片机发送数据给ESP8266模块,并通过Wi-Fi发送到远程服务器:
假设您的单片机使用UART与ESP8266通信,下面是一个示例代码:
#include
SoftwareSerial espSerial(2, 3); // RX, TX
void setup() {
Serial.begin(9600); // 串口监视器
espSerial.begin(115200); // ESP8266串口通信
}
void loop() {
// 从传感器中读取数据
int sensorData = analogRead(A0);
// 将数据转换为字符串
String dataToSend = String(sensorData);
// 发送AT指令给ESP8266模块
espSerial.println("AT+CIPSTART=\"TCP\",\"your_server_ip\",your_server_port");
delay(1000);
espSerial.println("AT+CIPSEND=" + String(dataToSend.length() + 2));
delay(500);
espSerial.println(dataToSend);
delay(5000); // 延时等待数据发送完成
// 关闭TCP连接
espSerial.println("AT+CIPCLOSE");
delay(1000);
delay(5000); // 每隔一段时间进行一次数据发送
}
在这个示例中,单片机通过软串口(SoftwareSerial)与ESP8266模块通信。它首先从传感器中读取数据,然后将数据转换为字符串并发送给ESP8266模块。单片机通过发送AT指令来控制ESP8266模块的行为,包括建立TCP连接、发送数据以及关闭连接。
请注意,您需要根据您的实际情况修改代码中的IP地址和端口号,并确保ESP8266模块已正确配置以连接到您的Wi-Fi网络。此外,由于示例代码中使用了延时函数,实际应用中可能需要根据需要进行更复杂的时间控制和错误处理。
typedef struct app
{
u8 wifiSta; // WiFi连接状态
u8 waterSta; // // 1.水泵开启或关闭
float temp; //温度
u8 turbidity; // 浑浊度
float ph; // PH值
u8 tds; // tds
}appData;
枚举类的隐式的定义
enum DAY
{
MON, //默认0
TUE, //默认1
WED, //默认2
THU, //默认3
FRI, //默认4
SAT, //默认5
SUN //默认6
};
1.在c语言中,枚举类中的是枚举成员,默认枚举成员默认是整数类型。
2.隐式定义时,第一个枚举成员的默认值为0,后续成员值依次递加1。
3.可以自定义枚举类型的值,从而自定义某个范围内的整数。
4.枚举型是预处理指令#define的替代。
枚举类的显式的定义
enum DAY
{
MON = 1, //显式指定值为1
TUE = 5, //显式指定值为5
WED = 19, //
THU = 27, //
FRI = 121, //
SAT = 123, //
SUN = 345 //
};
mygpio.h文件
typedef enum GPIONUM
{
BEEP, //蜂鸣器默认为0
PUMP, //水泵默认为1
GPIOMAX, //GPIOMAX默认为2
} GPIONUM;
typedef struct GPIOCOFIG
{
GPIO_TypeDef *GPIOx;
uint16_t GPIO_Pin;
uint32_t RCC_APB2Periph;
GPIOMode_TypeDef ioMode;
} GPIOCOFIG;
// 从app获取水泵数据0或1,然后设置水泵状态
if (g_appdata.waterSta == 1)
{
ggpio.gpio_set(PUMP, 1);
}
else
{
ggpio.gpio_set(PUMP, 0);
}
ggpio.gpio_set(PUMP, 1);这里对水泵进行了设置 ,实际是ggpio.gpio_set(1, 1);
->GPIO_SetBits(gpioconf[1].GPIOx, gpioconf[1].GPIO_Pin);
->GPIO_SetBits(GPIOB, GPIO_Pin_10); 可以看出水泵的引脚是B10,那蜂鸣器的引脚是A4
mygpio.c文件如下
#include "stm32f10x.h"
#include "mygpio.h"
#include "sys.h"
#include "delay.h"
#include "stdio.h"
#include "string.h"
#include "hmi_common.h"
#include "gizwits_protocol.h"
static void init(void);
static void gpio_scan(void);
static void gpio_set(GPIONUM gpiox, uint8_t levelid);
//定义了一个GPIOCOFIG 数组,数组名为gpioconf,GPIOMAX为2
static GPIOCOFIG gpioconf[GPIOMAX] = {
// 管脚组 管脚号 管脚组时钟 IOMODE
{GPIOA, GPIO_Pin_4, RCC_APB2Periph_GPIOA, GPIO_Mode_Out_PP},
{GPIOB, GPIO_Pin_10, RCC_APB2Periph_GPIOB, GPIO_Mode_Out_PP},
};
GPIO ggpio = {
.gpio_init = init,
.gpio_scan = gpio_scan,
.gpio_set = gpio_set,
};
uint8_t g_gpio_statu[GPIOMAX];
// 按键初始化函数
static void init(void) // IO初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
uint8_t i = 0;
for (i = 0; i < GPIOMAX; i++)
{
memset(&GPIO_InitStructure, 0, sizeof(GPIO_InitTypeDef));
RCC_APB2PeriphClockCmd(gpioconf[i].RCC_APB2Periph, ENABLE);
GPIO_InitStructure.GPIO_Pin = gpioconf[i].GPIO_Pin;
GPIO_InitStructure.GPIO_Mode = gpioconf[i].ioMode; // 浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(gpioconf[i].GPIOx, &GPIO_InitStructure); // 根据设定参数初始化
}
ggpio.gpio_set(BEEP, 1);
ggpio.gpio_set(PUMP, 0);
}
static void gpio_scan(void)
{
int i = 0;
for (i = 0; i < GPIOMAX; i++)
{
g_gpio_statu[i] = GPIO_ReadInputDataBit(gpioconf[i].GPIOx, gpioconf[i].GPIO_Pin);
}
}
static void gpio_set(GPIONUM gpiox, uint8_t level)
{
if (level == 1)
{
GPIO_SetBits(gpioconf[gpiox].GPIOx, gpioconf[gpiox].GPIO_Pin);
}
else
{
GPIO_ResetBits(gpioconf[gpiox].GPIOx, gpioconf[gpiox].GPIO_Pin);
}
}
dsl8b20的端口PB12
u8 DS18B20_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PORTG口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //PORTG.11 推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// GPIO_SetBits(0XFFF0FFFF,GPIO_Pin_4); //输出1
DS18B20_Rst();
return DS18B20_Check();
}
OLED SCL(串行时钟)接单片机的SCL-PB6 、SDA (串行数据)接单片机的SDA-PB7、VCC、GND
ESP8266 TX接PA10 RX接PA9
DS18B20 DQ接PB12、VCC、GND
Tds AO接PA6 、VCC、GND
Ph To接PB1 、 Po接什么不知道 、 VCC、GND
分为信号处理模块和电极
信号处理模块:信号处理模块负责接收来自探测电极的电信号,并进行放大、滤波和处理,以确保测量结果的稳定性和准确性。该模块可能还包括校准电路或接口,用于用户校准和调整。
探测电极:探测电极是实际进行pH测量的部分,通常由玻璃电极构成。它与待测液体接触,并产生反映液体pH值的电信号。
浑浊度 OUT接PB0、
蜂鸣器接IO接A4 VCC、GND
水泵红线接B10、黑线接GND


if (g_appdata.wifiSta == 1)
{
userHandle(); // 对结构体currentDataPoint中温度、温度阈值,ph、ph阈值,tds、tds阈值,水泵等进行设置
}
gizwitsHandle((dataPoint_t *)¤tDataPoint); }
/** 上传数据流程
|-gizwitsHandle() 241 # 把用户变化的数据上报
|-gizProtocolIssuedProcess(didPtr, gizwitsProtocol.protocolBuf+sizeof(protocolHead_t)+offset, protocolLen-(sizeof(protocolHead_t)+offset+1), ackData, &ackLen) 1520 #从云平台获取数据
|-gizDevReportPolicy(currentData) 1653 #
|-gizCheckReport(currentData, (dataPoint_t *)&gizwitsProtocol.gizLastDataPoint) 694 # 判断是否有数据发生变化需要上报
|-gizDataPoints2ReportData(currentData,gizwitsProtocol.reportData,(uint32_t *)&gizwitsProtocol.reportDataLen) 697 #把数据转为上报形式
|-gizReportData(ACTION_REPORT_DEV_STATUS, gizwitsProtocol.reportData, gizwitsProtocol.reportDataLen); 699 # 上报数据
*/
3.5 处理云端/APP发送过来的控制事件。
与控制型协议相关的函数调用关系如下:

函数调用说明:

3.6 上报设备状态

函数调用说明:

#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "key.h"
#include "mysoftiic.h"
#include "oled.h"
#include "gui.h"
#include "app.h"
#include "stmflash.h"
#include "menu.h"
#include "mainhmi.h"
#include "sms.h"
#include "timer.h"
#include "string.h"
#include "adc.h"
#include "mygpio.h"
#include "dht11.h"
#include "math.h"
#include "rtc.h"
#include "gizwits_product.h"
#include "gizwits_protocol.h"
#include "math.h"
#include "step.h"
#include "pwm.h"
#include "ds18b20.h"
#define KEY_SCAN_MS 20 // 20ms
#define MENU_SCAN_MS 50 // 50ms
#define ADC_GPIO_SCAN_MS 1000 // 2000ms
#define DHT11_SCAN_MS 2000 // 2000ms
#define BEEP_ALARM_MS 1000
#define SET_SACN_MS 1000
#define CLOUDPLATFORM_SCAN_MS 100 // 50ms
#define DS18B20_SCAN_MS 2000 // 50ms
// #define SMS_SCAN_MS 100 // 50ms
// uint8_t sms_szbuf[MAX_CMD_LEN];
// uint16_t sms_len;
MainMenuCfg_t tMainMenu;
static uint32_t key_scan_tick = 0;
static uint32_t menu_scan_tick = 0;
static uint32_t adc_or_gpio_tick = 0;
static uint32_t beep_alarm_tick = 0;
static uint32_t cloudPlatform_tick = 0;
static uint32_t dht11_tick = 0;
static uint32_t set_scan_tick = 0;
static uint32_t ds18b20_data_tick = 0;
// static uint32_t sms_alarm_tick = 0;
// static uint32_t sms_data_tick = 0;
const double EPS = 0.0000001;
// uint8_t alarmFlag = 0;
char sms_alarm_buf[256];
int main(void)
{
uint8_t alarmFlag = 0;
static u8 step_sta = 0;
u8 stepTimes = 0;
delay_init(); // 延时初始化
my_uart_recv_buf_init(); //串口接收缓冲区初始化
uart_init(115200); //用USART1与esp8266通信,PA9和PA10作为TX和RX
uart2_init(); //
// uart3_init();
gkey.key_init(); //键盘初始化
ggpio.gpio_init(); //GPIO初始化初始化
Adc_Init(); //ADC通道初始化
adc1_Start(); //ADC通道初始化
// dy_sv17f_init();
cfg_init(); //温度,tds,ph,浑浊度阈值初始化
gizwitsInit(); //机智云平台初始化
HAL_SOFT_I2C_Init(IIC_SOFT_NUM0, (uint32_t)GPIOB, GPIO_Pin_7, (uint32_t)GPIOB, GPIO_Pin_6); //给OLED开引脚PB6 SCL PB7为SDA
// HAL_SOFT_I2C_Init(IIC_SOFT_NUM0, (uint32_t)GPIOB, GPIO_Pin_9, (uint32_t)GPIOB, GPIO_Pin_8);
OLED_Init(); //OLED初始化
OLED_Clear();
GUI_ClearMap(0x00);
// 开机
GUI_Center_str(32, "水质检测系统", Normal);
GUI_ShowMap();
// RCT配置时钟,RTC初始化,RTC初始化成功才能进行下面的步骤,不然就一直等待
while(RTC_Init())
{
}
// 菜单初始化
tMainMenu.pszDesc = "主菜单";
tMainMenu.pszEnDesc = "Main Menu";
tMainMenu.pfnLoadCallFun = Hmi_LoadMainHmi;
tMainMenu.pfnRunCallFun = Hmi_MainTask;
Menu_Init(&tMainMenu);
//温度传感器初始化,设置数据引脚PB12
DS18B20_Init();
// ggpio.gpio_set(FAN, 0);
// ggpio.gpio_set(LED, 0);
// g_smsControl.sms_english_send("womenyazhou");
while (1)
{
//从我们的结构体g_appdata中获取水泵数据0或1,然后设置水泵状态
if (g_appdata.waterSta == 1)
{
ggpio.gpio_set(PUMP, 1);
}
else
{
ggpio.gpio_set(PUMP, 0);
}
// 每隔20ms获取一次按键
if (millis_elapsed(key_scan_tick) >= KEY_SCAN_MS)
{
key_scan_tick = systicks_get();
gkey.key_scan();
}
// 每隔50ms执行一次菜单任务函数
if (millis_elapsed(menu_scan_tick) >= MENU_SCAN_MS)
{
menu_scan_tick = systicks_get();
//执行菜单任务函数
Menu_Task();
}
//每隔1000ms从ADC通道或GPIO口传感器的数据
if (millis_elapsed(adc_or_gpio_tick) >= ADC_GPIO_SCAN_MS)
{
adc_or_gpio_tick = systicks_get();
//以下注释掉的代码是浑浊度的计算,同时把app数据的浑浊度进行更新
// g_adc_value[ADC_CH_8] = Get_Adc_Average(ADC_Channel_8, 5);
// g_appdata.turbidity = ((float)g_adc_value[ADC_CH_8] * (3.3 / 4096)) * 100 / 3.3;
// if (g_appdata.turbidity > 100)
// g_appdata.turbidity = 100;
//读取浑浊度,并把浑浊度封装到我们的结构体g_appdata中
g_appdata.turbidity = (1 - ((float)Get_Adc_Average(ADC_Channel_8, 5) / 4096)) * 100;
//读取ph值,并把ph值封装到我们的结构体g_appdata中
// g_appdata.ph = -5.7541 * (Get_Adc_Average(ADC_Channel_9, 5) * 3.3 / 4096) + 21.677; // 通过公式转换成PH值
// g_appdata.ph = -0.1738 * (Get_Adc_Average(ADC_Channel_9, 5) * 3.3 / 4096) + 3.2442; // 通过公式转换成PH值
g_appdata.ph = (3.2442 - (Get_Adc_Average(ADC_Channel_9, 5) * 3.3 / 4096)) / 0.1738;
if (g_appdata.ph <= 0)
g_appdata.ph = 0; // PH值小于0 矫正为0
else if (g_appdata.ph >= 14)
g_appdata.ph = 14; // PH值大于14 矫正为14
//读取tds值,并把tds封装到我们的结构体g_appdata中
g_appdata.tds = ((float)Get_Adc_Average(ADC_Channel_6, 5) / 4096) * 100;
}
// 每隔2000ms从传感器获取一次温度
if (millis_elapsed(ds18b20_data_tick) >= DS18B20_SCAN_MS)
{
ds18b20_data_tick = systicks_get();
g_appdata.temp = (float)DS18B20_Get_Temp() / 10; // 读取DS18B20温度数据
}
// 每隔1000ms判断一下蜂鸣器要不要报警
if (millis_elapsed(beep_alarm_tick) >= BEEP_ALARM_MS)
{
beep_alarm_tick = systicks_get();
//蜂鸣器警报标志
alarmFlag = 0;
//判断浑浊度,tds,ph,温度是否不在阈值范围内,如果不在就设置报警标志为1
if ((g_appdata.turbidity > cfg_data.hunzuo) && (g_appdata.turbidity != 0))
{
alarmFlag = 1;
}
else if ((g_appdata.tds > cfg_data.tds_max) && (g_appdata.tds != 0))
{
alarmFlag = 1;
}
else if (((g_appdata.ph < cfg_data.ph_min) || (g_appdata.ph > cfg_data.ph_max)) && ((g_appdata.ph - 0.0) > EPS))
{
alarmFlag = 1;
}
else if (((g_appdata.temp < cfg_data.temp_min) || (g_appdata.temp > cfg_data.temp_max)) && ((g_appdata.temp - 0.0) > EPS))
{
alarmFlag = 1;
}
// 根据报警标志触发蜂鸣器
if (alarmFlag == 1)
{
ggpio.gpio_set(BEEP, 0);
}
else
{
ggpio.gpio_set(BEEP, 1);
}
}
//每隔100ms上传并获取数据从云平台
if (millis_elapsed(cloudPlatform_tick) >= CLOUDPLATFORM_SCAN_MS)
{
cloudPlatform_tick = systicks_get();
if (g_appdata.wifiSta == 1)
{
userHandle(); //从传感器获取到的数据被封装到了g_appdata,这一步是把appdata中的值复制到currentDataPoint
}
gizwitsHandle((dataPoint_t *)¤tDataPoint);
}
}
}
结构体1
//自定义的临时存放传感器数据和从云平台拿到的水泵状态的结构体
typedef struct app
{
u8 wifiSta; // WiFi连接状态
u8 waterSta; // 1.水泵开启或关闭
float temp; //温度
u8 turbidity; // 浑浊度
float ph; // PH值
u8 tds; // tds
}appData;
结构体2
//把数据放在这个结构体中发给云平台
typedef struct {
bool valuepump;
uint32_t valuehunzhuodu;
float valueph;
uint32_t valuetds;
uint32_t valuehunzhuomax;
float valueph_max;
float valueph_min;
float valuetemp;
float valuetemp_max;
float valuetemp_min;
float valueTDSmax;
} dataPoint_t;
结构体3
从云平台获取到的数据就存放在gizCurrentDataPoint中
typedef struct
{
uint8_t issuedFlag; ///< P0 action type
uint8_t protocolBuf[MAX_PACKAGE_LEN]; ///< Protocol data handle buffer
uint8_t transparentBuff[MAX_PACKAGE_LEN]; ///< Transparent data storage area
uint32_t transparentLen; ///< Transmission data length
uint32_t sn; ///< Message SN
uint32_t timerMsCount; ///< Timer Count
protocolWaitAck_t waitAck; ///< Protocol wait ACK data structure
eventInfo_t issuedProcessEvent; ///< Control events
eventInfo_t wifiStatusEvent; ///< WIFI Status events
eventInfo_t NTPEvent; ///< NTP events
eventInfo_t moduleInfoEvent; ///< Module Info events
dataPointFlags_t waitReportDatapointFlag; ///< Store the data points to be reported flag
uint8_t reportData[sizeof(gizwitsElongateP0Form_t)]; ///< Reporting actual data , Max , Can hold all datapoints value
uint32_t reportDataLen; ///< Reporting actual data length
dataPoint_t gizCurrentDataPoint; ///< Current device datapoints status
dataPoint_t gizLastDataPoint; ///< Last device datapoints status
moduleStatusInfo_t wifiStatusData; ///< WIFI signal intensity
protocolTime_t TimeNTP; ///< Network time information
#if MODULE_TYPE
gprsInfo_t gprsInfoNews;
#else
moduleInfo_t wifiModuleNews; ///< WIFI module Info
#endif
}gizwitsProtocol_t;
方法int8_t gizwitsEventProcess(eventInfo_t *info, uint8_t *gizdata, uint32_t len)中把从云平台获取到的水泵设置状态放到我们自己的结构体中
//把云平台获取到的水泵的状态封装到我们的g_appdata结构体中
g_appdata.waterSta = currentDataPoint.valuepump;