一、项目需求

  1. 支持模拟多线程功能;
  2. 支持门禁功能;
  3. 支持燃气报警功能;
  4. 支持环境数据上传 OneNET 功能;
  5. 支持蓝牙、语音控制客厅/卧室灯;

在这里插入图片描述

二、硬件清单

  • 矩阵键盘
  • OLED
  • 蜂鸣器
  • W25Q128
  • 继电器
  • 烟雾传感器
  • DHT11
  • 光敏电阻传感器
  • SU-03T
  • 蓝牙模块
  • 杜邦线
  • STM32
  • ST-Link
  • USB转TTL

三、硬件接线

在这里插入图片描述

四、项目框图

在这里插入图片描述

五、完整代码

main.c

#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "beep.h"
#include "keyboard.h"
#include "lock.h"
#include "oled.h"
#include "w25q128.h"
#include "password.h"
#include "timer.h"
#include "tasks.h"
#include "adc.h"
#include "fan.h"
#include "bluetooth.h"
#include "voice.h"
#include "esp8266.h"
#include "onenet.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */    
    
    //串口1初始化
    uart1_init(115200);
    //蜂鸣器初始化
    beep_init();
    //矩阵键盘初始化
    keyboard_init();
    //电磁锁初始化
    lock_init();
    //OLED初始化
    oled_init();
    //密码模块初始化
    password_init();
    //定时器初始化
    timer_init(5000 - 1, 7200 - 1);     //500ms中断一次
    //ADC初始化
    adc_dma_init();
    //风扇初始化
    fan_init();
    //蓝牙初始化
    bt_init(115200);
    //LED初始化
    led_init();
    //语音模块初始化
    voice_init();
    //esp8266初始化
    esp8266_init(115200);
    //MQTT初始化
    printf("MQTT初始化...\r\n");
    mqtt_init();
    printf("MQTT连接...\r\n");
    mqtt_connect(MQTT_ClientID, MQTT_UserName, MQTT_PassWord);
    //串口打印测试
    printf("打印测试:hello world\r\n");
    
    //检查密码是否存在
    password_check();
    //按键最后一次输入值
    uint8_t key_last = 0;

    while(1)
    {
        //OLED显示输入
        oled_show_input();
        //获取最后一次输入值
        key_last = password_get_input();
        //如果最后一次输入值为#
        if(key_last == POUND_KEY){
            //密码比对
            if(password_compare() == TRUE){
                password_input_right_action();
            }else{
                password_input_wrong_action();
            }
        //如果最后一次输入值为*
        }else if(key_last == START_KEY){
            //显示输入旧密码
            oled_show_old();
            //获取输入
            password_get_input();
            //密码比对
            if(password_compare() == TRUE){
                password_old_right_action();
            }else{
                password_old_wrong_action();
            }
        }
    }
}

led.c

#include "led.h"
#include "sys.h"

TIM_HandleTypeDef pwm_handler = {0};;

//init函数
void led_livingroom_init(uint16_t arr, uint16_t psc)
{
    TIM_OC_InitTypeDef pwm_config = {0};
    
    //时基工作参数初始化
    pwm_handler.Instance = TIM4;
    pwm_handler.Init.Prescaler = psc;
    pwm_handler.Init.Period = arr;
    pwm_handler.Init.CounterMode = TIM_COUNTERMODE_UP;
    pwm_handler.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    
    HAL_TIM_PWM_Init(&pwm_handler); //自动调用msp函数
    
    //PWM模式配置
    pwm_config.OCMode = TIM_OCMODE_PWM1;
    pwm_config.Pulse = 0;
    pwm_config.OCPolarity = TIM_OCPOLARITY_HIGH;
    HAL_TIM_PWM_ConfigChannel(&pwm_handler,&pwm_config,TIM_CHANNEL_1);
    
    //使能输出,启动计数器
    HAL_TIM_PWM_Start(&pwm_handler,TIM_CHANNEL_1);
}

//msp函数
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM4){
        //GPIO初始化
        GPIO_InitTypeDef gpio_initstruct;
    
        //使能GPIOB时钟
        __HAL_RCC_GPIOB_CLK_ENABLE();
        //使能定时器4时钟
        __HAL_RCC_TIM4_CLK_ENABLE();
        
        //调用GPIO初始化函数
        gpio_initstruct.Pin = GPIO_PIN_6;               //PWM对应引脚
        gpio_initstruct.Mode = GPIO_MODE_AF_PP;         //复用推挽输出
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
        gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
        HAL_GPIO_Init(GPIOB, &gpio_initstruct);
    }
}

//修改CCR值的函数
void pwm_compare_set(uint16_t val)
{
    __HAL_TIM_SET_COMPARE(&pwm_handler,TIM_CHANNEL_1,val);
}

//客厅灯关闭
void led_livingroom_off(void)
{
    pwm_compare_set(0);
}

//客厅灯低亮
void led_livingroom_low(void)
{
    pwm_compare_set(150);
}

//客厅灯中亮
void led_livingroom_medium(void)
{
    pwm_compare_set(300);
}

//客厅灯高亮
void led_livingroom_high(void)
{
    pwm_compare_set(450);
}

//卧室灯初始化函数
void led_bedroom_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能GPIOA时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_11;              //卧室灯对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    HAL_GPIO_Init(GPIOA, &gpio_initstruct);
    
    //关闭卧室灯
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);
    
}

//点亮卧室灯的函数
void led_bedroom_on(void)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);   //拉低卧室灯引脚,点亮卧室灯
}


//熄灭卧室灯的函数
void led_bedroom_off(void)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);     //拉高卧室灯引脚,熄灭卧室灯
}

//翻转卧室灯的函数
void led_bedroom_toggle(void)
{
    HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_11);                   //翻转卧室灯引脚电平
}

//灯初始化
void led_init(void)
{
    //卧室灯初始化
    led_bedroom_init();
    //客厅灯初始化
    led_livingroom_init(500 - 1, 72 - 1);
}

led.h

#ifndef __LED_H__
#define __LED_H__

#include "sys.h"

//卧室灯初始化函数
void led_bedroom_init(void);
//点亮卧室灯的函数
void led_bedroom_on(void);
//熄灭卧室灯的函数
void led_bedroom_off(void);
//翻转卧室灯的函数
void led_bedroom_toggle(void);
//灯初始化
void led_init(void);

//init函数
void pwm_init(uint16_t arr, uint16_t psc);
//修改CCR值的函数
void pwm_compare_set(uint16_t val);
//客厅灯关闭
void led_livingroom_off(void);
//客厅灯低亮
void led_livingroom_low(void);
//客厅灯中亮
void led_livingroom_medium(void);
//客厅灯高亮
void led_livingroom_high(void);

#endif 

beep.c

#include "beep.h"
#include "sys.h"

//初始化GBIO口函数
void beep_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能GPIOC时钟
    __HAL_RCC_GPIOC_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_13;               //beep对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //上拉
    gpio_initstruct.Pull = GPIO_PULLUP;             //高速
    HAL_GPIO_Init(GPIOC, &gpio_initstruct);
    
    //关闭beep
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
}

//开启beep的函数
void beep_on(void)
{
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);   //拉低beep引脚,开启beep
}


//关闭beep的函数

void beep_off(void)
{
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);     //拉高beep引脚,关闭beep
}

//翻转beep的函数
void beep_toggle(void)
{
    HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);                   //翻转beep引脚电平
}

beep.h

#ifndef __BEEP_H__
#define __BEEP_H__

//初始化GBIO口函数
void beep_init(void);

//开启beep的函数
void beep_on(void);

//关闭beep的函数

void beep_off(void);

//翻转beep的函数
void beep_toggle(void);

#endif 

keyboard.c

#include "keyboard.h"
#include "delay.h"

static uint8_t key_value = 0;

//矩阵键盘初始化
void keyboard_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能GPIOA GPIOB时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_12;   //行对应引脚
    gpio_initstruct.Mode = GPIO_MODE_IT_FALLING;                                //中断下降沿触发
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;                               //高速
    gpio_initstruct.Pull = GPIO_PULLUP;                                         //上拉
    HAL_GPIO_Init(GPIOB, &gpio_initstruct);
    
    gpio_initstruct.Pin = GPIO_PIN_13 | GPIO_PIN_14;                            //列对应引脚
    gpio_initstruct.Mode = GPIO_MODE_INPUT;                                     //输入模式
    gpio_initstruct.Pull = GPIO_PULLDOWN;                                       //下拉
    HAL_GPIO_Init(GPIOB, &gpio_initstruct);
    
    gpio_initstruct.Pin = GPIO_PIN_8;                            //列对应引脚
    HAL_GPIO_Init(GPIOA, &gpio_initstruct);
    
    //配置中断
    HAL_NVIC_SetPriority(EXTI0_IRQn, 3, 0);//配置中断线
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);        //使能中断
    
    HAL_NVIC_SetPriority(EXTI1_IRQn, 3, 0);//配置中断线
    HAL_NVIC_EnableIRQ(EXTI1_IRQn);        //使能中断
    
    HAL_NVIC_SetPriority(EXTI2_IRQn, 3, 0);//配置中断线
    HAL_NVIC_EnableIRQ(EXTI2_IRQn);        //使能中断
    
    HAL_NVIC_SetPriority(EXTI15_10_IRQn, 3, 0);//配置中断线
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);        //使能中断
}
//中断线0的中断服务函数
void EXTI0_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}
//中断线1的中断服务函数
void EXTI1_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
}
//中断线2的中断服务函数
void EXTI2_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
}
//中断线12的中断服务函数
void EXTI15_10_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
}
//中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    uint8_t row = 0, column = 0;
    
    //如果扫描过程中再次按下则直接返回
    if(key_value != 0){
        return;
    }
    //确认行
    if(GPIO_Pin == GPIO_PIN_0){
        row = 0x10;
    }else if(GPIO_Pin == GPIO_PIN_1){
        row = 0x20;
    }else if(GPIO_Pin == GPIO_PIN_2){
        row = 0x30;
    }else if(GPIO_Pin == GPIO_PIN_12){
        row = 0x40;
    }
    
    //确认列
    if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == GPIO_PIN_SET){
        delay_ms(10);
        while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13)){
            column = 0x01;
        }
    }else if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_SET){
        delay_ms(10);
        while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14)){
            column = 0x02;
        }
    }else if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8) == GPIO_PIN_SET){
        delay_ms(10);
        while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)){
            column = 0x03;
        }
    }
    
    if(row != 0 && column != 0){
        key_value = row | column;
    }
}
//获取矩阵键盘值
uint8_t keyboard_get_value(void)
{
    uint8_t ch = 0;
    
    if(key_value != 0){
        if(key_value == 0x11) ch = '1';
        else if(key_value == 0x12) ch = '2';
        else if(key_value == 0x13) ch = '3';
        else if(key_value == 0x21) ch = '4';
        else if(key_value == 0x22) ch = '5';
        else if(key_value == 0x23) ch = '6';
        else if(key_value == 0x31) ch = '7';
        else if(key_value == 0x32) ch = '8';
        else if(key_value == 0x33) ch = '9';
        else if(key_value == 0x41) ch = '*';
        else if(key_value == 0x42) ch = '0';
        else if(key_value == 0x43) ch = '#';
        delay_ms(400);
        key_value = 0x00;
    }
    return ch;
}

keyboard.h

#ifndef __KEYBOARD_H__
#define __KEYBOARD_H__

#include "sys.h"

//矩阵键盘初始化
void keyboard_init(void);
//获取矩阵键盘值
uint8_t keyboard_get_value(void);

#endif 

lock.c

#include "lock.h"
#include "sys.h"

//初始化GBIO口函数
void lock_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能GPIOB时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_7;               //LOCK对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //上拉
    gpio_initstruct.Pull = GPIO_PULLUP;             //高速
    HAL_GPIO_Init(GPIOB, &gpio_initstruct);
    
    //关闭LOCK
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
}

//打开LOCK的函数
void lock_on(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);   //拉低LOCK引脚,打开LOCK
}


//关闭LOCK的函数
void lock_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);     //拉高LOCK引脚,关闭LOCK
}

//翻转LOCK的函数
void lock_toggle(void)
{
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_7);                   //翻转LED1引脚电平
}

//返回LOCK的状态
uint8_t lock_status_get(void)
{
    return (uint8_t)HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7);
}

lock.h

#ifndef __LOCK_H__
#define __LOCK_H__

#include "stdint.h"

#define LCOK_STATUS_ON 0
#define LOCK_STATUS_OFF 0

//初始化GBIO口函数
void lock_init(void);

//点亮lock的函数
void lock_on(void);

//熄灭lock的函数
void lock_off(void);

//翻转lock的函数
void lock_toggle(void);

//返回继电器的状态
uint8_t lock_status_get(void);

#endif 

oled.c

#include "oled.h"
#include "delay.h"
#include "font.h"

//OLED相关GPIO初始化
void oled_gpio_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能SCL和SDA引脚时钟
    OLED_I2C_SCL_CLK();
    OLED_I2C_SDA_CLK();
    
    //GPIO初始化配置
    gpio_initstruct.Pin = OLED_I2C_SCL_PIN;         //SCL对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    HAL_GPIO_Init(OLED_I2C_SCL_PORT, &gpio_initstruct);
    
    gpio_initstruct.Pin = OLED_I2C_SDA_PIN;         //SDA对应引脚
    HAL_GPIO_Init(OLED_I2C_SDA_PORT, &gpio_initstruct);
}
//I2C起始信号
void oled_i2c_start(void)
{
    OLED_SCL_SET();
    OLED_SDA_SET();
    OLED_SDA_RESET();
    OLED_SCL_RESET();
}
//I2C停止信号
void oled_i2c_stop(void)
{
    OLED_SCL_SET();
    OLED_SDA_RESET();
    OLED_SDA_SET();
}
//I2C应答信号
void oled_i2c_ack(void)
{
    OLED_SCL_SET();
    OLED_SCL_RESET();
}
//I2C写字节
void oled_i2c_write_byte(uint8_t data)
{
    uint8_t i, tmp;
    tmp = data;
    //往SDA总线上循环写数据位,高位先行
    for(i = 0; i <8 ;i++){
        //取出最高位
        if((tmp & 0x80) == 0x80){
            OLED_SDA_SET();
        }else{
            OLED_SDA_RESET();
        }
        //逻辑左移一位,去除次高位
        tmp = tmp << 1;
        OLED_SCL_SET();
        OLED_SCL_RESET();
    }
}
//OLED写命令
void oled_write_cmd(uint8_t cmd)
{
    oled_i2c_start();
    oled_i2c_write_byte(0x78);
    oled_i2c_ack();
    oled_i2c_write_byte(0x00);
    oled_i2c_ack();
    oled_i2c_write_byte(cmd);
    oled_i2c_ack();
    oled_i2c_stop();
}
//OLED写数据
void oled_write_data(uint8_t data)
{
    oled_i2c_start();
    oled_i2c_write_byte(0x78);
    oled_i2c_ack();
    oled_i2c_write_byte(0x40);
    oled_i2c_ack();
    oled_i2c_write_byte(data);
    oled_i2c_ack();
    oled_i2c_stop();
}
//OLED初始化
void oled_init(void)
{
    oled_gpio_init();
    
    delay_ms(100);
    
    oled_write_cmd(0xAE);    //设置显示开启/关闭,0xAE关闭,0xAF开启

    oled_write_cmd(0xD5);    //设置显示时钟分频比/振荡器频率
    oled_write_cmd(0x80);    //0x00~0xFF

    oled_write_cmd(0xA8);    //设置多路复用率
    oled_write_cmd(0x3F);    //0x0E~0x3F

    oled_write_cmd(0xD3);    //设置显示偏移
    oled_write_cmd(0x00);    //0x00~0x7F

    oled_write_cmd(0x40);    //设置显示开始行,0x40~0x7F

    oled_write_cmd(0xA1);    //设置左右方向,0xA1正常,0xA0左右反置

    oled_write_cmd(0xC8);    //设置上下方向,0xC8正常,0xC0上下反置

    oled_write_cmd(0xDA);    //设置COM引脚硬件配置
    oled_write_cmd(0x12);

    oled_write_cmd(0x81);    //设置对比度
    oled_write_cmd(0xCF);    //0x00~0xFF

    oled_write_cmd(0xD9);    //设置预充电周期
    oled_write_cmd(0xF1);

    oled_write_cmd(0xDB);    //设置VCOMH取消选择级别
    oled_write_cmd(0x30);

    oled_write_cmd(0xA4);    //设置整个显示打开/关闭

    oled_write_cmd(0xA6);    //设置正常/反色显示,0xA6正常,0xA7反色

    oled_write_cmd(0x8D);    //设置充电泵
    oled_write_cmd(0x14);

    oled_write_cmd(0xAF);    //开启显示
    
    oled_fill(0x00);         //清空屏幕
}

//设置坐标
void oled_set_cursor(uint8_t x, uint8_t y)
{
    //指定待写入页
    oled_write_cmd(0xB0 + y);
    //指定待写入列
    oled_write_cmd((x & 0x0F) | 0x00);
    oled_write_cmd((x & 0xF0) >> 4 | 0x10);
}
//循环填充
void oled_fill(uint8_t data)
{
    uint8_t i,j;
    
    for(i = 0;i < 8;i++){
        oled_set_cursor(0, i);      //指定一次页,写一次列自动往后偏移
        for(j = 0;j < 128;j++){
            oled_write_data(data);
        }
    }
}
//OLED显示一个字符
void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size)
{
    uint8_t i,j, page;
    //ASCII码相对第一个空格字符偏移
    num = num - ' ';
    //确定字符所占页数
    page = size / 8;
    if(size % 8){
        page++;
    }
    //循环刷新屏幕
    for(j = 0;j < page;j++){
        //设定字符坐标
        oled_set_cursor(x, y + j);
        //分行写数据
        for(i = size/2 * j;i < size/2 * (j+1);i++){
            if(size == 12){
                oled_write_data(ascii_6X12[num][i]);
            }else if(size == 16){
                oled_write_data(ascii_8X16[num][i]);
            }else if(size == 24){
                oled_write_data(ascii_12X24[num][i]);
            }
        }
    }
}
//OLED显示字符串
void oled_show_string(uint8_t x, uint8_t y, char *p, uint8_t size)
{
    while(*p != '\0'){
        oled_show_char(x, y, *p, size);
        x += size/2;
        p++;
    }
}
//OLED显示汉字
//void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N, uint8_t size)
//{
//    uint8_t i,j, page;
//    
//    //确定汉字所占页数
//    page = size / 8;
//    if(size % 8){
//        page++;
//    }
//    //循环刷新屏幕
//    for(j = 0;j < page;j++){
//        //设定字符坐标
//        oled_set_cursor(x, y + j);
//        //分行写数据
//        for(i = size * j;i < size * (j+1);i++){
//            if(size == 16){
//                oled_write_data(chinese_16x16[N][i]);
//            }else if(size == 24){
//                oled_write_data(chinese_24x24[N][i]);
//            }
//        }
//    }
//}
//OLED显示汉字
void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N, uint8_t message_type)
{
    uint16_t i,j;
    for(j = 0;j < 2;j++){
        oled_set_cursor(x, y + j);
        for(i = 16 * j;i < 16 * (j + 1);i++){
            switch(message_type){
                case SHOW_INPUT_PWD:
                    oled_write_data(chinese_enter_password[N][i]);
                    break;
                case SHOW_PWD_RIGHT:
                    oled_write_data(chinese_password_right[N][i]);
                    break;
                case SHOW_PWD_WRONG:
                    oled_write_data(chinese_password_wrong[N][i]);
                    break;
                case SHOW_INPUT_OLD_PWD:
                    oled_write_data(chinese_enter_old_password[N][i]);
                    break;
                case SHOW_INPUT_NEW_PWD:
                    oled_write_data(chinese_enter_new_password[N][i]);
                    break;
                case SHOW_PWD_CHANGED:
                    oled_write_data(chinese_password_changed[N][i]);
                    break;
                case SHOW_SET_PWD:
                    oled_write_data(chinese_set_password[N][i]);
                    break;
            }
        }
    }
}

//请输入密码
void oled_show_input(void)
{
    oled_fill(0x00);         //清空屏幕
    oled_show_chinese(10, 1, 0, SHOW_INPUT_PWD);
    oled_show_chinese(30, 1, 1, SHOW_INPUT_PWD);
    oled_show_chinese(50, 1, 2, SHOW_INPUT_PWD);
    oled_show_chinese(70, 1, 3, SHOW_INPUT_PWD);
    oled_show_chinese(90, 1, 4, SHOW_INPUT_PWD);
    oled_show_char(110, 1, ':', 16);
}
//密码正确
void oled_show_right(void)
{
    oled_fill(0x00);         //清空屏幕
    oled_show_chinese(10, 1, 0, SHOW_PWD_RIGHT);
    oled_show_chinese(30, 1, 1, SHOW_PWD_RIGHT);
    oled_show_chinese(50, 1, 2, SHOW_PWD_RIGHT);
    oled_show_chinese(70, 1, 3, SHOW_PWD_RIGHT);
}
//密码错误
void oled_show_wrong(void)
{
    oled_fill(0x00);         //清空屏幕
    oled_show_chinese(10, 1, 0, SHOW_PWD_WRONG);
    oled_show_chinese(30, 1, 1, SHOW_PWD_WRONG);
    oled_show_chinese(50, 1, 2, SHOW_PWD_WRONG);
    oled_show_chinese(70, 1, 3, SHOW_PWD_WRONG);
}
//请输入旧密码
void oled_show_old(void)
{
    oled_fill(0x00);         //清空屏幕
    oled_show_chinese(10, 1, 0, SHOW_INPUT_OLD_PWD);
    oled_show_chinese(30, 1, 1, SHOW_INPUT_OLD_PWD);
    oled_show_chinese(50, 1, 2, SHOW_INPUT_OLD_PWD);
    oled_show_chinese(70, 1, 3, SHOW_INPUT_OLD_PWD);
    oled_show_chinese(90, 1, 4, SHOW_INPUT_OLD_PWD);
    oled_show_chinese(110, 1, 5, SHOW_INPUT_OLD_PWD);
}
//请输入新密码
void oled_show_new(void)
{
    oled_fill(0x00);         //清空屏幕
    oled_show_chinese(10, 1, 0, SHOW_INPUT_NEW_PWD);
    oled_show_chinese(30, 1, 1, SHOW_INPUT_NEW_PWD);
    oled_show_chinese(50, 1, 2, SHOW_INPUT_NEW_PWD);
    oled_show_chinese(70, 1, 3, SHOW_INPUT_NEW_PWD);
    oled_show_chinese(90, 1, 4, SHOW_INPUT_NEW_PWD);
    oled_show_chinese(110, 1, 5, SHOW_INPUT_NEW_PWD);
}
//密码修改成功
void oled_show_changed(void)
{
    oled_fill(0x00);         //清空屏幕
    oled_show_chinese(10, 1, 0, SHOW_PWD_CHANGED);
    oled_show_chinese(30, 1, 1, SHOW_PWD_CHANGED);
    oled_show_chinese(50, 1, 2, SHOW_PWD_CHANGED);
    oled_show_chinese(70, 1, 3, SHOW_PWD_CHANGED);
    oled_show_chinese(90, 1, 4, SHOW_PWD_CHANGED);
    oled_show_chinese(110, 1, 5, SHOW_PWD_CHANGED);
}
//请设定密码
void oled_show_set(void)
{
    oled_fill(0x00);         //清空屏幕
    oled_show_chinese(10, 1, 0, SHOW_SET_PWD);
    oled_show_chinese(30, 1, 1, SHOW_SET_PWD);
    oled_show_chinese(50, 1, 2, SHOW_SET_PWD);
    oled_show_chinese(70, 1, 3, SHOW_SET_PWD);
    oled_show_chinese(90, 1, 4, SHOW_SET_PWD);
    oled_show_char(110, 1, ':', 16);
}
//OLED显示图片
void oled_show_image(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t *bmp)
{
    uint8_t i, j;
    for(j = 0; j < height; j++)
    {
        oled_set_cursor(x, y + j);
        for(i = 0; i < width; i++)
            oled_write_data(bmp[width * j + i]);
    }
}

oled.h

#ifndef __OLED_H__
#define __OLED_H__

#include "sys.h"

//定义密码处理显示字符枚举类型
enum message{
    SHOW_INPUT_PWD = 0,
    SHOW_PWD_RIGHT,
    SHOW_PWD_WRONG,
    SHOW_INPUT_OLD_PWD,
    SHOW_INPUT_NEW_PWD,
    SHOW_PWD_CHANGED,
    SHOW_SET_PWD
};

#define OLED_I2C_SCL_CLK()      __HAL_RCC_GPIOB_CLK_ENABLE()
#define OLED_I2C_SCL_PORT       GPIOB
#define OLED_I2C_SCL_PIN        GPIO_PIN_8

#define OLED_I2C_SDA_CLK()      __HAL_RCC_GPIOB_CLK_ENABLE()
#define OLED_I2C_SDA_PORT       GPIOB
#define OLED_I2C_SDA_PIN        GPIO_PIN_9

#define OLED_SCL_RESET()        HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SCL_PIN,GPIO_PIN_RESET)
#define OLED_SCL_SET()        HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SCL_PIN,GPIO_PIN_SET)

#define OLED_SDA_RESET()        HAL_GPIO_WritePin(OLED_I2C_SDA_PORT,OLED_I2C_SDA_PIN,GPIO_PIN_RESET)
#define OLED_SDA_SET()        HAL_GPIO_WritePin(OLED_I2C_SDA_PORT,OLED_I2C_SDA_PIN,GPIO_PIN_SET)

//OLED初始化
void oled_init(void);
//I2C写命令
void oled_write_cmd(uint8_t cmd);
//I2C写数据
void oled_write_data(uint8_t data);
//循环填充
void oled_fill(uint8_t data);
//设置坐标
void oled_set_cursor(uint8_t x, uint8_t y);
//OLED显示一个字符
void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size);
//OLED显示字符串
void oled_show_string(uint8_t x, uint8_t y, char *p, uint8_t size);
//OLED显示汉字
void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N, uint8_t size);
//OLED显示图片
void oled_show_image(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t *bmp);
//请输入密码
void oled_show_input(void);
//密码正确
void oled_show_right(void);
//密码错误
void oled_show_wrong(void);
//请输入旧密码
void oled_show_old(void);
//请输入新密码
void oled_show_new(void);
//密码修改成功
void oled_show_changed(void);
//请设定密码
void oled_show_set(void);

#endif

w25q128.c

#include "w25q128.h"

SPI_HandleTypeDef spi_handle = {0};
//SPI初始化
void w25q128_spi_init(void)
{
    spi_handle.Instance = SPI1;                                     //使用SPI1
    spi_handle.Init.Mode = SPI_MODE_MASTER;                         //作为主设备
    spi_handle.Init.Direction = SPI_DIRECTION_2LINES;               //全双工传输
    spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;                   //数据位长度位8bit
    spi_handle.Init.CLKPolarity = SPI_POLARITY_LOW;                 //时钟极性为低电平
    spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE;                     //奇数边沿采样
    spi_handle.Init.NSS = SPI_NSS_SOFT;                             //软件控制
    spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;  //波特率256分频
    spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;                    //高位先行
    spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;
    spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    spi_handle.Init.CRCPolynomial = 7;
    HAL_SPI_Init(&spi_handle);
}

//SPI硬件相关初始化
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
    if(hspi->Instance == SPI1){
        GPIO_InitTypeDef gpio_initstruct;
    
        //使能GPIOA时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();
        //使能SPI1时钟
        __HAL_RCC_SPI1_CLK_ENABLE();
        
        //调用GPIO初始化函数
        gpio_initstruct.Pin = GPIO_PIN_4;               //NSS对应引脚
        gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
        gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
        
        gpio_initstruct.Pin = GPIO_PIN_5 | GPIO_PIN_7;  //CLK、MOSI对应引脚
        gpio_initstruct.Mode = GPIO_MODE_AF_PP;         //复用推挽输出
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
        
        gpio_initstruct.Pin = GPIO_PIN_6;              //MISO对应引脚
        gpio_initstruct.Mode = GPIO_MODE_INPUT;        //输入模式
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
    }
}
//SPI字节数据交换(数据收发)
uint8_t w25q128_spi_swap_byte(uint8_t data)
{
    uint8_t recv_data = 0;
    
    HAL_SPI_TransmitReceive(&spi_handle, &data, &recv_data, 1, 1000);   //数据收发
    return recv_data;
}
//W25Q128初始化
void w25q128_init(void)
{
    w25q128_spi_init();
}
//读取设备ID
uint16_t w25q128_read_id(void)
{
    uint16_t device_id = 0;
    //拉低片选
    W25Q128_CS(0);
    //写指令
    w25q128_spi_swap_byte(FLASH_ManufactDeviceID);
    w25q128_spi_swap_byte(0x00);
    w25q128_spi_swap_byte(0x00);
    w25q128_spi_swap_byte(0x00);
    //获取device_id
    device_id = w25q128_spi_swap_byte(FLASH_DummyByte) << 8;
    device_id |= w25q128_spi_swap_byte(FLASH_DummyByte);
    //拉高片选
    W25Q128_CS(1);
    return device_id;
}
//W25Q128写使能
void w25q128_write_enable(void)
{
    //拉低片选
    W25Q128_CS(0);
    //写入指令
    w25q128_spi_swap_byte(FLASH_WriteEnable);
    //拉高片选
    W25Q128_CS(1);
    
}
//W25Q128读取SR1寄存器
uint8_t w25q128_read_sr1(void)
{
    uint8_t recv_data = 0;
    
    //拉低片选
    W25Q128_CS(0);
    //发送指令
    w25q128_spi_swap_byte(FLASH_ReadStatusReg1);
    //读取SR1
    recv_data = w25q128_spi_swap_byte(FLASH_DummyByte);
    //拉高片选
    W25Q128_CS(1);
    
    return recv_data;
}

//发送地址
void w25q128_send_address(uint32_t address)
{
    //从高位到低位依次发送
    w25q128_spi_swap_byte(address >> 16);
    w25q128_spi_swap_byte(address >> 8);
    w25q128_spi_swap_byte(address);
}

//W25Q128读数据
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size)
{
    uint32_t i = 0;
    
     //拉低片选
    W25Q128_CS(0);
    //发送指令
    w25q128_spi_swap_byte(FLASH_ReadData);
    //发送地址
    w25q128_send_address(address);
    //读取SR1
    for(i = 0;i < size;i++){
        data[i] = w25q128_spi_swap_byte(FLASH_DummyByte);
    }
    
    //拉高片选
    W25Q128_CS(1);
}

//忙等待
void w25q128_wait_busy(void)
{
    while((w25q128_read_sr1() & 0x01) == 0x01);
}

//W25Q128页写
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size)
{
    uint16_t i = 0;
    
    //写使能
    w25q128_write_enable();
    //拉低片选
    W25Q128_CS(0);
    //发送指令
    w25q128_spi_swap_byte(FLASH_PageProgram);
    //发送地址
    w25q128_send_address(address);
    //写入数据
    for(i = 0;i < size;i++){
        w25q128_spi_swap_byte(data[i]);
    }
    //拉高片选
    W25Q128_CS(1);
    //忙等待
    w25q128_wait_busy();
}
//W25Q128擦除扇区
void w25q128_erase_sector(uint32_t address)
{
    //写使能
    w25q128_write_enable();
    //忙等待
    w25q128_wait_busy();
    //拉低片选
    W25Q128_CS(0);
    //发送指令
    w25q128_spi_swap_byte(FLASH_SectorErase);
    //发送地址
    w25q128_send_address(address);
    //拉高片选
    W25Q128_CS(1);
    //忙等待
    w25q128_wait_busy();
}

w25q128.h

#ifndef __W25Q128_H__
#define __W25Q128_H__

#include "sys.h"

#define W25Q128_CS(x)   do{ x ?  \
                                 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET) : \
                                 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); \
                        }while(0)
//指令表
#define FLASH_ManufactDeviceID      0x90
#define FLASH_WriteEnable           0x06
#define FLASH_ReadStatusReg1        0x05
#define FLASH_ReadData              0x03
#define FLASH_PageProgram           0x02
#define FLASH_SectorErase           0x20
#define FLASH_DummyByte             0xFF

//W25Q128初始化
void w25q128_init(void);
//读取设备ID
uint16_t w25q128_read_id(void);
//W25Q128读数据
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size);
//忙等待
void w25q128_wait_busy(void);
//W25Q128页写
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size);
//W25Q128擦除扇区
void w25q128_erase_sector(uint32_t address);

                        
#endif

password.c

#include "password.h"
#include "w25q128.h"
#include "oled.h"
#include "keyboard.h"
#include "string.h"
#include "stdio.h"
#include "lock.h"
#include "beep.h"
#include "delay.h"

//密码长度宏定义
#define PASSWORD_SIZE 10
//输入字符串
uint8_t pwd_input[PASSWORD_SIZE] = {0};
//读出字符串
uint8_t pwd_read[PASSWORD_SIZE] = {0};
//矩阵键盘输入缓存
uint8_t key_value = 0;
//定义数组索引
uint8_t i = 0;
//密码输入错误次数
uint8_t try_times = 0;

//初始化密码
void password_init(void)
{
    //W25Q128初始化
    w25q128_init();
}

//清空输入缓存
void password_input_clear(void)
{
    memset(pwd_input, 0, PASSWORD_SIZE);
    i = 0;
}

//保存密码
void password_save(void)
{
    //擦除待写入扇区
    w25q128_erase_sector(0x000000);
    //写入输入的密码
    w25q128_write_page(0x000000, pwd_input, PASSWORD_SIZE);
    //OLED屏幕显示密码已修改
    oled_show_changed();
}

//获取键盘输入
uint8_t password_get_input(void)
{
    //清空输入缓存
    password_input_clear();
    while(1){
        //获取输入字符
        key_value =  keyboard_get_value();
        //判断输入字符逻辑
        if(key_value == POUND_KEY){
            printf("按下了#键,input: %s\r\n", pwd_input);
            return POUND_KEY;
        }else if(key_value == START_KEY){
            printf("按下了*键\r\n");
            return START_KEY;
        }else if(key_value != 0){
            printf("按下了 %c\r\n", key_value);
            //OLED逐个字符显示
            oled_show_char(20 + i * 10, 4, key_value, 16);
            //存入输入缓存区
            pwd_input[i++] = key_value;
        }
    }
}

//密码比对
uint8_t password_compare(void)
{
   uint8_t i = 0;
    
    //读出W25Q128中的密码
    w25q128_read_data(0x000000, pwd_read, PASSWORD_SIZE);
    //比对密码
    //判断长度
    if(strlen((char *)pwd_input) != strlen((char *)pwd_read)){
        return FALSE;
    }
    //逐位比较
    for(i = 0;i < strlen((char *)pwd_read); i++){
        if(pwd_input[i] != pwd_read[i]){
            return FALSE;
        }
    }
    
    return TRUE;
}

//密码输入正确的操作
void password_input_right_action(void)
{
    oled_show_right();
    lock_on();
    beep_on();
    delay_ms(500);
    beep_off();
    delay_ms(1000);
    lock_off();
    //错误次数清零
    try_times = 0;
}

//密码输入错误的操作
void password_input_wrong_action(void)
{
    oled_show_wrong();
    try_times++;
    //错误超过三次
    if(try_times >= 3){
        beep_on();
        delay_ms(1000);
        beep_off();
        try_times = 0;
    }
    delay_ms(1000);
}

//旧密码输入正确的操作
void password_old_right_action(void)
{
    oled_show_new();
    password_get_input();
    password_save();
    beep_on();
    delay_ms(500);
    beep_off();
    delay_ms(500);
}

//旧密码输入错误的操作
void password_old_wrong_action(void)
{
    oled_show_wrong();
    delay_ms(1000);
}

//检查密码文件是否存在
void password_check(void)
{
    w25q128_read_data(0x000000, pwd_read, PASSWORD_SIZE);
    printf("读出密码:%s\r\n", pwd_read);
    //判断密码是否存在
    if(pwd_read[0] == '\0' || pwd_read[0] == 0xFF){
        oled_show_set();
        password_get_input();
        password_save();
    }
}

password.h

#ifndef __PASSWORD_H__
#define __PASSWORD_H__

#include "sys.h"

#define POUND_KEY   '#'
#define START_KEY   '*'

#define TRUE    1
#define FALSE   0

//初始化密码
void password_init(void);
//清空输入缓存
void password_input_clear(void);
//保存密码
void password_save(void);
//获取键盘输入
uint8_t password_get_input(void);
//密码比对
uint8_t password_compare(void);
//密码输入正确的操作
void password_input_right_action(void);
//密码输入错误的操作
void password_input_wrong_action(void);
//旧密码输入正确的操作
void password_old_right_action(void);
//旧密码输入错误的操作
void password_old_wrong_action(void);
//检查密码文件是否存在
void password_check(void);

#endif

timer.c

#include "timer.h"
#include "tasks.h"

TIM_HandleTypeDef timer_handle = {0};

//定时器初始化函数
void timer_init(uint16_t arr,uint16_t psc)
{
    //配置定时器结构体
    timer_handle.Instance = TIM2;
    timer_handle.Init.Prescaler = psc;
    timer_handle.Init.Period = arr;
    timer_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    timer_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    HAL_TIM_Base_Init(&timer_handle);
    //使能更新中断,启动计数器
    HAL_TIM_Base_Start_IT(&timer_handle);
}

//msp初始化函数,在HAL_TIM_Base_Init中调用,无须自己调用
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2){
        //开启时钟
        __HAL_RCC_TIM2_CLK_ENABLE();
        //设置中断优先级
        HAL_NVIC_SetPriority(TIM2_IRQn,2,2);
        //使能中断线
        HAL_NVIC_EnableIRQ(TIM2_IRQn);
    }
}

//中断服务函数
void TIM2_IRQHandler(void)
{
    //公共处理函数
    HAL_TIM_IRQHandler(&timer_handle);
}


//更新中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM2){
        systick_isr();
    }
}

timer.h

#ifndef __TIMER_H__
#define __TIMER_H__

#include "sys.h"

void timer_init(uint16_t arr,uint16_t psc);

#endif

tasks.c

#include "tasks.h"
#include "led.h"
#include "stdio.h"
#include "adc.h"
#include "beep.h"
#include "fan.h"
#include "bluetooth.h"
#include "led.h"
#include "voice.h"
#include "dht11.h"
#include "string.h"
#include "onenet.h"

//定义各线程时间计数器
uint32_t task1_cnt = 0;
uint32_t task2_cnt = 0;
uint32_t task3_cnt = 0;

//定义线程标志
uint8_t task1_flag = 0;
uint8_t task2_flag = 0;
uint8_t task3_flag = 0;

//定义adc值
uint16_t adc_value[2] = {0};
//定义led状态
uint8_t led_status = 0;
//定义语音新状态标志
uint8_t voice_status_new = 0;
//定义语音旧状态标志
uint8_t voice_status_old = 0;


//500ms中断一次
void systick_isr(void)
{
  if(task1_cnt < 1){
    task1_cnt++;
  }else{
    task1_flag = 1;
    task1_cnt = 0;
  }
  
  if(task2_cnt < 2){
    task2_cnt++;
  }else{
    task2_flag = 1;
    task2_cnt = 0;
  }
  
  if(task3_cnt < 120){
    task3_cnt++;
  }else{
    task3_flag = 1;
    task3_cnt = 0;
  }
  //三个任务“并行”执行
  task_light_control();
  task_gas_alarm();
  task_env_upload();
}

//蓝牙语音控制灯线程
void task_light_control(void)
{
    if(task1_flag == 0){
        return;
    }
    task1_flag = 0;
    //获取蓝牙控制LED值
    led_status = bt_value_get();
    //获取语音控制LED值
    voice_status_new = voice_value_get();
    //若产生新值才更新led_status
    if(voice_status_new != voice_status_old){
        led_status = voice_status_new;
        voice_status_old = voice_status_new;
    }
    
    //根据LED状态标志改变灯的状态
    switch(led_status){
        case 0:         
            led_livingroom_off();       //客厅灯关闭
            printf("客厅灯关闭\r\n");
            break;
        case 1:
            led_livingroom_low();       //客厅灯低亮
            printf("客厅灯低亮\r\n");
            break;
        case 2:
            led_livingroom_medium();    //客厅灯中亮
            printf("客厅灯中亮\r\n");
            break;
        case 3:
            led_livingroom_high();      //客厅灯高亮
            printf("客厅灯高亮\r\n");
            break;
        case 4:
            led_bedroom_on();           //卧室灯打开
            break;
        case 5:
            led_bedroom_off();          //卧室灯关闭
            break;
        default:
            
            break;
    }
}

//燃气报警检测线程
void task_gas_alarm(void)
{
    if(task2_flag == 0){
        return;
    }
    task2_flag = 0;
    
    //获取燃气值
    adc_result_get(adc_value);
    float gas_value = (float)adc_value[0]/4096*3.3;
    //printf("燃气值:%0.2f\r\n", gas_value);
    if(gas_value > 1.0){
        beep_on();      //报警
        fan_on();       //开风扇
    }else{
        beep_off();     //不报警
        fan_off();      //关风扇
    }
}

//温湿度燃气数据上传onenet平台线程
void task_env_upload(void)
{
    if(task3_flag == 0){
        return;
    }
    task3_flag = 0;
    
    //定义待上传JSON字符串
    uint8_t data_send_buf[512];
    
    //定义并获取温湿度数据
    uint8_t dht11_data[4];
    dht11_read(dht11_data);
    //printf("湿度:%d.%dRH,",dht11_data[0],dht11_data[1]);
    //printf("温度:%d.%d℃",dht11_data[2],dht11_data[3]);
    //定义光照强度(由于没有光敏电阻模块,所以也采用烟雾数据)
    float light_value = (float)adc_value[0]/4096*3.3;
    //清空JSON字符串
    memset(data_send_buf, 0, sizeof(data_send_buf));
    //构造JSON字符串
    memset(data_send_buf, 0, sizeof(data_send_buf));
    sprintf((char *)data_send_buf, "{\"id\":\"1720777789227\",\"version\":\"1.0\",\"params\":{\"CurrentTemperature\":{\"value\":%d.%d},\"RelativeHumidity\":{\"value\":%d.%d},\"LightLuxValue\":{\"value\":%.1f}}}"
        , dht11_data[2], dht11_data[3], dht11_data[0], dht11_data[1], light_value);
    //上传至onenet平台
    mqtt_publish_data(POST_TOPIC, (char *)data_send_buf, 0);
}

tasks.h

#ifndef __TASKS_H__
#define __TASKS_H__

#include "sys.h"

void systick_isr(void);
void task_light_control(void);
void task_gas_alarm(void);
void task_env_upload(void);

#endif 

adc.c

#include "adc.h"
#include "string.h"

uint16_t adc_result[2] = {0};

//ADC句柄
ADC_HandleTypeDef adc_handle = {0};
//DMA句柄
DMA_HandleTypeDef dma_handle = {0};

//ADC配置
void adc_config(void)
{
    adc_handle.Instance = ADC1;                            //选择ADC1
    adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;       //数据右对齐
    adc_handle.Init.ScanConvMode = ADC_SCAN_ENABLE;        //扫描
    adc_handle.Init .ContinuousConvMode = ENABLE;          //连续转换
    adc_handle.Init.NbrOfConversion = 2;                   //转换个数为2
    adc_handle.Init.DiscontinuousConvMode = DISABLE;       //不采用间断模式           
    adc_handle.Init.NbrOfDiscConversion = 0;               //间断模式个数为0
    adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; //采用软件触发
    
    HAL_ADC_Init(&adc_handle);  //调用初始化函数
    
    //ADC校准
    HAL_ADCEx_Calibration_Start(&adc_handle);
}
//DMA配置
void dma_config()
{
    __HAL_RCC_DMA1_CLK_ENABLE();
    
    dma_handle.Instance = DMA1_Channel1;                //DMA1通道5
    dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;   //从外设到内存
    //内存相关配置
    dma_handle.Init   .MemDataAlignment = DMA_MDATAALIGN_HALFWORD;    //数据对齐方式
    dma_handle.Init.MemInc = DMA_MINC_ENABLE;                         //数据增长方式
    //外设相关配置
    dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //数据对齐方式
    dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;                  //数据增长方式
    //优先级和模式
    dma_handle.Init.Priority =DMA_PRIORITY_MEDIUM;
    dma_handle.Init.Mode = DMA_CIRCULAR;    //循环搬运
    //初始化函数
    HAL_DMA_Init(&dma_handle);
    
    //链接串口和DMA
    __HAL_LINKDMA(&adc_handle, DMA_Handle, dma_handle);
}
//ADC相关硬件配置
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    //确认是否为ADC1
    if(hadc->Instance == ADC1){
        RCC_PeriphCLKInitTypeDef  adc_clk_init = {0}; 
        GPIO_InitTypeDef gpio_init_struct = {0};
        //开启ADC1时钟
        __HAL_RCC_ADC1_CLK_ENABLE();
        //使能GPIOA时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();
        //设置PIN1口,模拟输入模式
        gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;
        //GPIO初始化
        HAL_GPIO_Init(GPIOA,&gpio_init_struct);
        //设置外设时钟选择为RCC外设ADC时钟
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;
        //设置adc时钟分频因子 为6分频
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;
        //配置外设时钟
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
    }
} 
//ADC通道配置
void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch,uint32_t rank, uint32_t stime)
{ 
    ADC_ChannelConfTypeDef adc_ch_config = {0};
    //设置adc通道参数
    adc_ch_config.Channel = ch;     //设置通道
    adc_ch_config.Rank = rank;      //设置通道次序
    adc_ch_config.SamplingTime = stime;     //设置采样时间
    //通道配置
    HAL_ADC_ConfigChannel(hadc, &adc_ch_config);
}
//获取ADC的值
uint32_t adc_get_result(uint32_t ch)
{
    //配置ADC通道
    adc_channel_config(&adc_handle, ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
    //开始ADC转换
    HAL_ADC_Start(&adc_handle);
    //轮询方式等待ADC转换完成
    HAL_ADC_PollForConversion(&adc_handle, 10);
    //获取ADC转换结果
    return (uint16_t)HAL_ADC_GetValue(&adc_handle);
}
//ADC和DMA初始化
void adc_dma_init(void)
{
    //ADC配置
    adc_config();
    //配置ADC通道
    adc_channel_config(&adc_handle, ADC_CHANNEL_0, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
    adc_channel_config(&adc_handle, ADC_CHANNEL_1, ADC_REGULAR_RANK_2, ADC_SAMPLETIME_239CYCLES_5);
    //DMA配置
    dma_config();
    //开始ADC-DMA转换
    HAL_ADC_Start_DMA(&adc_handle, (uint32_t *)adc_result, 2);
}

//传出adc结果
void adc_result_get(uint16_t *result)
{
    memcpy(result, adc_result, sizeof(adc_result));
}

adc.h

#ifndef __ADC_H__
#define __ADC_H__

#include "sys.h"

//ADC和DMA初始化
void adc_dma_init(void);
//传出adc结果
void adc_result_get(uint16_t *result);

#endif

fan.c

#include "fan.h"
#include "sys.h"

//初始化GBIO口函数
void fan_init(void)
{ 
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能GPIOA时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_12;               //fan对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //上拉
    gpio_initstruct.Pull = GPIO_PULLUP;             //高速
    HAL_GPIO_Init(GPIOA, &gpio_initstruct);
    
    //关闭plugin
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);
}

//打开fan的函数
void fan_on(void)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);   //拉低fan引脚,关闭fan
}


//关闭fan的函数

void fan_off(void)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);     //拉高fan引脚,关闭fan
}

//翻转fan的函数
void fan_toggle(void)
{
    HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_12);                   //翻转fan引脚电平
}

//返回fan的状态
uint8_t fan_status_get(void)
{
    return (uint8_t)HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12);
}

fan.h

#ifndef __FAN_H__
#define __FAN_H__

#include "stdint.h"

#define FAN_STATUS_ON 0
#define FAN_STATUS_OFF 0

//初始化GBIO口函数
void fan_init(void);

//打开fan的函数
void fan_on(void);

//关闭fan的函数
void fan_off(void);

//翻转fan的函数
void fan_toggle(void);

//返回继电器的状态
uint8_t fan_status_get(void);

#endif 

bluetooth.c

#include "sys.h"
#include "bluetooth.h"
#include "string.h"
#include "stdarg.h"

UART_HandleTypeDef uart2_handle;                                            /* uart2句柄 */

uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE];                                    /* uart2接收缓冲区 */
uint16_t uart2_rx_len = 0;                                                  /* uart2接收字符长度 */

//定义指令数字
uint8_t bt_rx = 0;

/**
 * @brief       串口1初始化函数
 * @param       baudrate: 波特率, 根据自己需要设置波特率值
 * @retval      无
 */
void bt_init(uint32_t baudrate)
{
    /*UART2 初始化设置*/
    uart2_handle.Instance = USART2;                                         /* USART2 */
    uart2_handle.Init.BaudRate = baudrate;                                  /* 波特率 */
    uart2_handle.Init.WordLength = UART_WORDLENGTH_8B;                      /* 字长为8位数据格式 */
    uart2_handle.Init.StopBits = UART_STOPBITS_1;                           /* 一个停止位 */
    uart2_handle.Init.Parity = UART_PARITY_NONE;                            /* 无奇偶校验位 */
    uart2_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;                      /* 无硬件流控 */
    uart2_handle.Init.Mode = UART_MODE_TX_RX;                               /* 收发模式 */
    HAL_UART_Init(&uart2_handle);                                           /* HAL_UART_Init()会使能UART2 */
}

/**
 * @brief       UART2接收缓冲区清除
 * @param       无
 * @retval      无
 */
void uart2_rx_clear(void)
{
    memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf));                          /* 清空接收缓冲区 */
    uart2_rx_len = 0;                                                       /* 接收计数器清零 */
}

/**
 * @brief       串口1中断服务函数
 * @note        在此使用接收中断及空闲中断,实现不定长数据收发
 * @param       无
 * @retval      无
 */
void USART2_IRQHandler(void)
{
    uint8_t receive_data = 0;   
    if(__HAL_UART_GET_FLAG(&uart2_handle, UART_FLAG_RXNE) != RESET){        /* 获取接收RXNE标志位是否被置位 */
        if(uart2_rx_len >= sizeof(uart2_rx_buf))                            /* 如果接收的字符数大于接收缓冲区大小, */
            uart2_rx_len = 0;                                               /* 则将接收计数器清零 */
        HAL_UART_Receive(&uart2_handle, &receive_data, 1, 1000);            /* 接收一个字符 */
        uart2_rx_buf[uart2_rx_len++] = receive_data;                        /* 将接收到的字符保存在接收缓冲区 */
    }

    if (__HAL_UART_GET_FLAG(&uart2_handle, UART_FLAG_IDLE) != RESET)        /* 获取接收空闲中断标志位是否被置位 */
    {
        printf("recv: %s\r\n", uart2_rx_buf);                               /* 将接收到的数据打印出来 */
        
        bt_rx = uart2_rx_buf[0];
        
        uart2_rx_clear();
        __HAL_UART_CLEAR_IDLEFLAG(&uart2_handle);                           /* 清除UART总线空闲中断 */
    }
}

////串口2发送函数
//void bt_send(char *send_buf, uint8_t size)
//{
//    HAL_UART_Transmit(&uart2_handle, (uint8_t*)send_buf, size, 100);
//}

//串口2发送不定长数据
void bt_send(char *format, ...)
{
    uint8_t send_buf[128] = {0};
    va_list arg;
    va_start(arg, format);
    vsprintf((char *)send_buf, format ,arg);
    va_end(arg);
    HAL_UART_Transmit(&uart2_handle, send_buf, sizeof(send_buf), 100);
}

//传出指令值
uint8_t bt_value_get(void)
{
    uint8_t temp;
    temp = bt_rx - '0';
    bt_rx = 0;
    return temp;
}

bluetooth.h

#ifndef __BLUETOOTH_H__
#define __BLUETOOTH_H__

#include "stdio.h"
#include "sys.h"

/* UART收发缓冲大小 */
#define UART2_RX_BUF_SIZE            128
#define UART2_TX_BUF_SIZE            64


void bt_init(uint32_t bound);            /* 串口初始化函数 */
////串口2发送函数
//void bt_send(char *send_buf, uint8_t size);
//串口2发送不定长数据
void bt_send(char *format, ...);

#endif

voice.c

#include "voice.h"

//语音模块初始化
void voice_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能GPIOA时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_5 | GPIO_PIN_4;  //卧室灯对应引脚
    gpio_initstruct.Mode = GPIO_MODE_INPUT;         //输入模式
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    HAL_GPIO_Init(GPIOB, &gpio_initstruct);
}

//语音模块值返回
uint8_t voice_value_get(void)
{
    return ((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4) << 1) | HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5));
}

voice.h

#ifndef __VOICE_H__
#define __VOICE_H__

#include "sys.h"


//语音模块初始化
void voice_init(void);
//语音模块值返回
uint8_t voice_value_get(void);

#endif  

dht11.c

#include "dht11.h"
#include "delay.h"
#include "string.h"
#include "stdio.h"

//定义温湿度存放变量
char dht11_data[5] = {0};    

//设置DHT11GPIO输入
void dht11_gpio_input(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能DHT11端口时钟
    DHT11_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = DHT11_PIN;                //DHT11对应引脚
    gpio_initstruct.Mode = GPIO_MODE_INPUT;         //输入
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}

//设置DHT11GPIO输出
void dht11_gpio_output(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能DHT11端口时钟
    DHT11_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = DHT11_PIN;                //DHT11对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;         //输入
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}

//DHT11启动
void dht11_start(void)
{
    //启动时序
    dht11_gpio_output();
    DHT11_DQ_OUT(1);
    DHT11_DQ_OUT(0);
    delay_ms(20);
    DHT11_DQ_OUT(1);
    
    dht11_gpio_input();
    while(DHT11_DQ_IN);     //等待DHT11拉低电平
    while(!DHT11_DQ_IN);   //等待DHT11拉高电平
    while(DHT11_DQ_IN);     //等待DHT11拉低电平
}

//DHT11GPIO读一个字节
uint8_t dht11_read_byte(void)
{
    uint8_t temp = 0;
    uint8_t i = 0;
    uint8_t read_data = 0;
    
    //循环读取8位
    for(i = 0; i < 8; i++){
        //读取位时序
        while(!DHT11_DQ_IN);   //等待DHT11拉高电平
        delay_us(50);
        if(DHT11_DQ_IN == 1){
            temp = 1;
            while(DHT11_DQ_IN);     //等待DHT11拉低电平
        }else{
            temp = 0;
        }
        //循环放入读取缓存
        read_data = read_data << 1;
        read_data |= temp;
    }
    
    return read_data;
}

//DHT11读数据
void dht11_read(uint8_t *result)
{
    uint8_t i = 0;
    
    __HAL_RCC_AFIO_CLK_ENABLE();    //使能AFIO时钟
    __HAL_AFIO_REMAP_SWJ_NOJTAG();  //禁用JTAG接口
    
    //启动DHT11
    dht11_start();
    //设置GPIO输入模式
    dht11_gpio_input();
    
    //循环读取5次字节
    for(i = 0; i < 5; i++){
        dht11_data[i] = dht11_read_byte();
    }
    
    if(dht11_data[0] + dht11_data[1] + dht11_data[2] + dht11_data[3] == dht11_data[4]){
        memcpy(result, dht11_data, 4);
        printf("\r\n");
        printf("湿度:%d.%dRH,",dht11_data[0],dht11_data[1]);
        printf("温度:%d.%d℃",dht11_data[2],dht11_data[3]);
        printf("\r\n");
    }
    
    delay_ms(2000);
}

dht11.h

#ifndef __DHT11_H__
#define __DHT11_H__

#include "sys.h"

#define DHT11_PORT          GPIOB
#define DHT11_PIN           GPIO_PIN_3
#define DHT11_CLK_ENABLE()  __HAL_RCC_GPIOB_CLK_ENABLE()

#define DHT11_DQ_OUT(x)     do{ x ? \
                               HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN , GPIO_PIN_SET) : \
                               HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN , GPIO_PIN_RESET);\
                              }while(0)

#define DHT11_DQ_IN         HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)                     

//DHT11读数据
void dht11_read(uint8_t *result);
                              
#endif

esp8266.c

#include "esp8266.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"
#include "stdarg.h"

//定义发送缓存区
uint8_t esp8266_tx_buf[ESP8266_TX_BUF_SIZE] = {0};
//定义接收缓存区
uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE] = {0};
//定义接收长度
uint16_t esp8266_rx_len = 0;
//定义esp8266最新接收数量,数量旧值
uint16_t esp8266_cnt = 0, esp8266_cnt_Pre = 0;

UART_HandleTypeDef esp8266_handle = {0};

//esp8266串口初始化函数
void esp8266_uart_init(uint32_t baudrate)
{
    //串口1选择
    esp8266_handle.Instance = USART3;
    //波特率
    esp8266_handle.Init.BaudRate = baudrate;
    //字长
    esp8266_handle.Init.WordLength = UART_WORDLENGTH_8B;
    //停止位
    esp8266_handle.Init.StopBits = UART_STOPBITS_1;
    //校验位
    esp8266_handle.Init.Parity = UART_PARITY_NONE;
    //流控
    esp8266_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    //模式
    esp8266_handle.Init.Mode = UART_MODE_TX_RX;
    //初始化
    HAL_UART_Init(&esp8266_handle);
}

//esp8266中断服务函数
void USART3_IRQHandler(void)
{
    //定义接收字符
    uint8_t receive_data = 0;
    //当接收寄存器不为空的时候
    if(__HAL_UART_GET_FLAG(&esp8266_handle, UART_FLAG_RXNE) != RESET){
        if(esp8266_cnt >= sizeof(esp8266_rx_buf)){
            //接受数量置为零
            esp8266_cnt = 0;
        }
        //接收一个字符
        HAL_UART_Receive(&esp8266_handle, &receive_data, 1,1000);
        esp8266_rx_buf[esp8266_cnt++] = receive_data;
        //再发送出去
        //HAL_UART_Transmit(&esp8266_handle,&receive_data, 1,1000);
    }
}

//等待接收函数
uint8_t esp8266_wait_receive(void)
{
    //如果接收数为零,则返回错误码
    if(esp8266_cnt == 0){
        return ESP8266_ERROR;
    }
    //如果接收数量不变,则表示本段接收完成,返回完成码
    if(esp8266_cnt == esp8266_cnt_Pre){
        esp8266_cnt = 0;
        return ESP8266_EOK;
    }
    //接受数量变了,则将新值赋给旧值,但是没接收完
    esp8266_cnt_Pre = esp8266_cnt;
    return ESP8266_ERROR;
}

//接收区清空
void esp8266_rx_clear(void)
{
    memset(esp8266_rx_buf, 0, sizeof(esp8266_rx_buf));
    esp8266_cnt = 0;
}

////esp8266发送
//void esp8266_send_data(char *fmt, ...)
//{
//    // 定义可变参数列表变量
//    va_list ap;
//    // 定义长度变量,用于存储字符串长度
//    uint16_t len;
//    // 初始化可变参数列表,ap指向fmt后的第一个参数
//    va_start(ap, fmt);
//    // 使用vsprintf将格式化后的字符串输出到发送缓冲区
//    // vsprintf会自动处理可变参数,根据fmt格式字符串将参数格式化后存入缓冲区
//    vsprintf((char* )esp8266_tx_buf, fmt, ap);
//    // 计算格式化后字符串的长度(不包括字符串结束符'\0')
//    len = strlen((const char *)esp8266_tx_buf);
//    // 通过UART发送数据
//    HAL_UART_Transmit(&esp8266_handle, esp8266_tx_buf, len, 100);
//}

//esp8266发送
void esp8266_send_data(char *data, uint16_t len)
{
    esp8266_rx_clear();
    HAL_UART_Transmit(&esp8266_handle, (unsigned char*)data, len, 100);
}

//拷贝RXDATA内容
uint16_t esp8266_copy_rxdata(char *data)
{
    memcpy(data, esp8266_rx_buf, esp8266_cnt_Pre);
    return esp8266_cnt_Pre;
}

//esp8266接收
void esp8266_receive_data(void)
{
    if(esp8266_wait_receive() == ESP8266_EOK){
        printf("esp8266 rev:%s\r\n",esp8266_rx_buf);
        esp8266_rx_clear();
    }
}

//esp8266发送函数
uint8_t esp8266_send_command(char *cmd, char *res)
{
    uint8_t time_out = 250;
    
    //清空接收缓存区
    esp8266_rx_clear();
    //发送函数
    HAL_UART_Transmit(&esp8266_handle,(uint8_t *)cmd, strlen(cmd),100);
    //判断超时
    while(time_out--){
        //判断是否接收到新数据
        if(esp8266_wait_receive() == ESP8266_EOK){
            //判断接受的数据是否和要求比较的数据一致
            if(strstr((char *)esp8266_rx_buf, res) != NULL){
                return ESP8266_EOK;
            }
        }
        delay_ms(10);
    }
    return ESP8266_ERROR;
}

//测试
uint8_t esp8266_at_test(void)
{
    return esp8266_send_command("AT\r\n", "OK");
}

//设置工作模式
uint8_t esp8266_set_mode(uint8_t mode)
{
    switch(mode){
        case ESP8266_STA_MODE:
            return esp8266_send_command("AT+CWMODE=1\r\n", "OK");
        case ESP8266_AP_MODE:
            return esp8266_send_command("AT+CWMODE=2\r\n", "OK");
        case ESP8266_STA_AP_MODE:
            return esp8266_send_command("AT+CWMODE=3\r\n", "OK");
        default:
            return ESP8266_EINVAL;
    }
}

//连接网络
uint8_t esp8266_join_ap(char *ssid, char *pwd)
{
    char cmd[64];
    
    sprintf(cmd,"AT+CWJAP=\"%s\",\"%s\"\r\n",ssid,pwd);
    return esp8266_send_command(cmd,"WIFI GOT IP");
}

//设置连接模式
uint8_t esp8266_connection_mode(uint8_t mode)
{
    char cmd[64];
    
    sprintf(cmd,"AT+CIPMUX=%d\r\n",mode);
    return esp8266_send_command(cmd,"OK");
}

//连接服务器
uint8_t esp8266_connect_tcp_server(char *server_ip, char *server_port)
{
    char cmd[64];
    sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%s\",%s\r\n", server_ip, server_port);
    return esp8266_send_command(cmd,"CONNECT");
}

//进入透传模式
uint8_t esp8266_enter_unvarnishied(void)
{
    uint8_t ret;
    ret = esp8266_send_command("AT+CIPMODE=1\r\n","OK");
    ret += esp8266_send_command("AT+CIPSEND\r\n",">");
    if(ret == ESP8266_EOK){
        return ESP8266_EOK;
    }else{
        return ESP8266_ERROR;
    }   
}

//esp8266初始化
void esp8266_init(uint32_t baudrate)
{
    printf("esp8266初始化开始...\r\n");
    //串口初始化
    esp8266_uart_init(baudrate);
    
    //配置wifi模块
    printf("1. 测试esp8266是否存在\r\n");
    while(esp8266_at_test()){
        delay_ms(500);
    }
    printf("2. 设置工作模式为STA...\r\n");
    while(esp8266_set_mode(ESP8266_STA_MODE)){
        delay_ms(500);
    }
    printf("3. 设置单链路链接模式...\r\n");
    while(esp8266_connection_mode(ESP8266_SINGLE_CONNECTION)){
        delay_ms(500);
    }
    printf("4. 连接wifi,SSID:%s, PWD:%s...\r\n",WIFI_SSID,WIFI_PWD);
     while(esp8266_join_ap(WIFI_SSID,WIFI_PWD)){
        delay_ms(2000);
    }
    printf("5. 连接云服务器,server_ip:%s,server_port:%s...\r\n",TCP_SERVER_IP,TCP_SERVER_PORT);
    while(esp8266_connect_tcp_server(TCP_SERVER_IP, TCP_SERVER_PORT)){
        delay_ms(500);
    }
    printf("6. 进入透传模式...\r\n");
    while(esp8266_enter_unvarnishied()){
        delay_ms(500);
    }
    printf("ESP8266已经连接服务器并进入透传模式\r\n");
    printf("ESP8266初始化完成\r\n");
}

////测试函数
//void esp8266_test(void)
//{
//    esp8266_send_data("This is from esp8266\r\n",20);
//    esp8266_receive_data();
//}

esp8266.h

#ifndef __ESP8266_H__
#define __ESP8266_H__

#include "stdio.h"
#include "sys.h"

//定义发送接收缓存大小
#define ESP8266_RX_BUF_SIZE 128
#define ESP8266_TX_BUF_SIZE 64

//定义状态标志
#define ESP8266_EOK                 0
#define ESP8266_ERROR               1
#define ESP8266_ETIMEOUT            2
#define ESP8266_EINVAL              3

//定义AT指令填充字段
#define ESP8266_STA_MODE            1
#define ESP8266_AP_MODE             2
#define ESP8266_STA_AP_MODE         3

#define ESP8266_SINGLE_CONNECTION   0
#define ESP8266_MULTI_CONNECTION    1

#define WIFI_SSID                   "HUAWEI Mate 60 Pro"
#define WIFI_PWD                    "88888888"

#define TCP_SERVER_IP               "mqtts.heclouds.com"
#define TCP_SERVER_PORT             "1883"

//esp8266初始化
void esp8266_init(uint32_t baudrate);
//esp8266接收
void esp8266_receive_data(void);
//测试函数
void esp8266_test(void);
//esp8266发送
void esp8266_send_data(char *data, uint16_t len);
//拷贝RXDATA内容
uint16_t esp8266_copy_rxdata(char *data);
//等待接收函数
uint8_t esp8266_wait_receive(void);

#endif

onenet.c

#include "onenet.h"
#include "esp8266.h"

char MQTT_ClientID[100]; //MQTT_客户端ID
char MQTT_UserName[100]; //MQTT_用户名
char MQTT_PassWord[200]; //MQTT_密码

uint8_t *mqtt_rxbuf;
uint8_t *mqtt_txbuf;
uint16_t mqtt_rxlen;
uint16_t mqtt_txlen;
uint8_t _mqtt_txbuf[512];//发送数据缓存区
uint8_t _mqtt_rxbuf[512];//接收数据缓存区

typedef enum
{
    //名字         值             报文流动方向     描述
    M_RESERVED1    =0    ,    //    禁止    保留
    M_CONNECT        ,    //    客户端到服务端    客户端请求连接服务端
    M_CONNACK        ,    //    服务端到客户端    连接报文确认
    M_PUBLISH        ,    //    两个方向都允许    发布消息
    M_PUBACK        ,    //    两个方向都允许    QoS 1消息发布收到确认
    M_PUBREC        ,    //    两个方向都允许    发布收到(保证交付第一步)
    M_PUBREL        ,    //    两个方向都允许    发布释放(保证交付第二步)
    M_PUBCOMP        ,    //    两个方向都允许    QoS 2消息发布完成(保证交互第三步)
    M_SUBSCRIBE        ,    //    客户端到服务端    客户端订阅请求
    M_SUBACK        ,    //    服务端到客户端    订阅请求报文确认
    M_UNSUBSCRIBE    ,    //    客户端到服务端    客户端取消订阅请求
    M_UNSUBACK        ,    //    服务端到客户端    取消订阅报文确认
    M_PINGREQ        ,    //    客户端到服务端    心跳请求
    M_PINGRESP        ,    //    服务端到客户端    心跳响应
    M_DISCONNECT    ,    //    客户端到服务端    客户端断开连接
    M_RESERVED2        ,    //    禁止    保留
}_typdef_mqtt_message;

//连接成功服务器回应 20 02 00 00
//客户端主动断开连接 e0 00
const uint8_t parket_connetAck[] = {0x20,0x02,0x00,0x00};
const uint8_t parket_disconnet[] = {0xe0,0x00};
const uint8_t parket_heart[] = {0xc0,0x00};
const uint8_t parket_heart_reply[] = {0xc0,0x00};
const uint8_t parket_subAck[] = {0x90,0x03};

/*
函数功能: 初始化阿里云物联网服务器的登录参数
*/


//密码
//加密之前的数据格式:  clientId*deviceName*productKey#
// *替换为DeviceName  #替换为ProductKey  加密密钥是DeviceSecret  加密方式是HmacSHA1  
//PassWord明文=  clientIdiot_devicedeviceNameiot_deviceproductKeya1VMIfYeEEE
//hmacsha1加密网站:http://encode.chahuo.com/
//加密的密钥:DeviceSecret

void mqtt_login_init(char *ProductKey,char *DeviceName,char *DeviceSecret)
{
//    sprintf(MQTT_ClientID,"%s.%s|securemode=2,signmethod=hmacsha256,timestamp=1695871022945|",ProductKey,DeviceName);
//    sprintf(MQTT_UserName,"%s&%s",DeviceName,ProductKey);
//    sprintf(MQTT_PassWord,"%s","a8921500839307ec3fedbbcd8c0cbc19f133f68c831dcad41fe13d92dc90b89d");
    sprintf(MQTT_ClientID,"%s", DeviceName);
    sprintf(MQTT_UserName,"%s", ProductKey);
    //sprintf(MQTT_PassWord,"version=2018-10-31&res=products%%2F%s%%2Fdevices%%2F%s&et=2017881776&method=sha1&sign=%s",ProductKey,DeviceName,DEVICE_SECRET);
    strcpy(MQTT_PassWord,"version=2018-10-31&res=products%2F0t8qlhHwDA%2Fdevices%2Fmyhome&et=2082789772&method=sha1&sign=6IoPlYQEsVXCykOUJckvoCjQq%2BM%3D");
}

void mqtt_init(void)
{
    mqtt_login_init(PRODUCT_KEY,DEVICE_NAME,DEVICE_SECRET);
    //缓冲区赋值
    mqtt_rxbuf = _mqtt_rxbuf;
    mqtt_rxlen = sizeof(_mqtt_rxbuf);
    mqtt_txbuf = _mqtt_txbuf;
    mqtt_txlen = sizeof(_mqtt_txbuf);
    memset(mqtt_rxbuf,0,mqtt_rxlen);
    memset(mqtt_txbuf,0,mqtt_txlen);
    
    //无条件先主动断开
    mqtt_disconnect();
    delay_ms(100);
    mqtt_disconnect();
    delay_ms(100);
}

/*
函数功能: 登录服务器
函数返回值: 0表示成功 1表示失败
*/
uint8_t mqtt_connect(char *ClientID,char *Username,char *Password)
{
//    uint8_t i;
    uint8_t j;
    int ClientIDLen = strlen(ClientID);
    int UsernameLen = strlen(Username);
    int PasswordLen = strlen(Password);
    int DataLen;
    mqtt_txlen=0;
    //可变报头+Payload  每个字段包含两个字节的长度标识
    DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2);
    
    //固定报头
    //控制报文类型
    mqtt_txbuf[mqtt_txlen++] = 0x10;        //MQTT Message Type CONNECT
    //剩余长度(不包括固定头部)
    do
    {
        uint8_t encodedByte = DataLen % 128;
        DataLen = DataLen / 128;
        // if there are more data to encode, set the top bit of this byte
        if ( DataLen > 0 )
            encodedByte = encodedByte | 128;
        mqtt_txbuf[mqtt_txlen++] = encodedByte;
    }while ( DataLen > 0 );
        
    //可变报头
    //协议名
    mqtt_txbuf[mqtt_txlen++] = 0;            // Protocol Name Length MSB    
    mqtt_txbuf[mqtt_txlen++] = 4;           // Protocol Name Length LSB    
    mqtt_txbuf[mqtt_txlen++] = 'M';            // ASCII Code for M    
    mqtt_txbuf[mqtt_txlen++] = 'Q';            // ASCII Code for Q    
    mqtt_txbuf[mqtt_txlen++] = 'T';            // ASCII Code for T    
    mqtt_txbuf[mqtt_txlen++] = 'T';            // ASCII Code for T    
    //协议级别
    mqtt_txbuf[mqtt_txlen++] = 4;                // MQTT Protocol version = 4    
    //连接标志
    mqtt_txbuf[mqtt_txlen++] = 0xc2;            // conn flags 
    mqtt_txbuf[mqtt_txlen++] = 0;                // Keep-alive Time Length MSB    
    mqtt_txbuf[mqtt_txlen++] = 100;            // Keep-alive Time Length LSB  100S心跳包  
    
    mqtt_txbuf[mqtt_txlen++] = BYTE1(ClientIDLen);// Client ID length MSB    
    mqtt_txbuf[mqtt_txlen++] = BYTE0(ClientIDLen);// Client ID length LSB      
    memcpy(&mqtt_txbuf[mqtt_txlen],ClientID,ClientIDLen);
    mqtt_txlen += ClientIDLen;
    
    if(UsernameLen > 0)
    {   
        mqtt_txbuf[mqtt_txlen++] = BYTE1(UsernameLen);        //username length MSB    
        mqtt_txbuf[mqtt_txlen++] = BYTE0(UsernameLen);        //username length LSB    
        memcpy(&mqtt_txbuf[mqtt_txlen],Username,UsernameLen);
        mqtt_txlen += UsernameLen;
    }
    
    if(PasswordLen > 0)
    {    
        mqtt_txbuf[mqtt_txlen++] = BYTE1(PasswordLen);        //password length MSB    
        mqtt_txbuf[mqtt_txlen++] = BYTE0(PasswordLen);        //password length LSB  
        memcpy(&mqtt_txbuf[mqtt_txlen],Password,PasswordLen);
        mqtt_txlen += PasswordLen; 
    }    
    
//    for(i=0;i<10;i++)
//    {
        memset(mqtt_rxbuf,0,mqtt_rxlen);
        mqtt_send_data(mqtt_txbuf,mqtt_txlen);
//        for(j=0;j<10;j++)
//            printf("%c",mqtt_txbuf[j]);
        for(j=0;j<10;j++)
        {
            delay_ms(50);
            if (esp8266_wait_receive() == ESP8266_EOK)
                esp8266_copy_rxdata((char *)mqtt_rxbuf);

            //CONNECT
            if(mqtt_rxbuf[0]==parket_connetAck[0] && mqtt_rxbuf[1]==parket_connetAck[1] && mqtt_rxbuf[2]==parket_connetAck[2]) //连接成功
            {
                return 0;//连接成功
            }
        }
//    }
    return 1;
}

/*
函数功能: MQTT订阅/取消订阅数据打包函数
函数参数:
    topic       主题   
    qos         消息等级 0:最多分发一次  1: 至少分发一次  2: 仅分发一次
    whether     订阅/取消订阅请求包 (1表示订阅,0表示取消订阅)
返回值: 0表示成功 1表示失败
*/
uint8_t mqtt_subscribe_topic(char *topic,uint8_t qos,uint8_t whether)
{    
//    uint8_t i;
    uint8_t j;
    mqtt_txlen=0;
    int topiclen = strlen(topic);
    
    int DataLen = 2 + (topiclen+2) + (whether?1:0);//可变报头的长度(2字节)加上有效载荷的长度
    //固定报头
    //控制报文类型
    if(whether)mqtt_txbuf[mqtt_txlen++] = 0x82; //消息类型和标志订阅
    else    mqtt_txbuf[mqtt_txlen++] = 0xA2;    //取消订阅

    //剩余长度
    do
    {
        uint8_t encodedByte = DataLen % 128;
        DataLen = DataLen / 128;
        // if there are more data to encode, set the top bit of this byte
        if ( DataLen > 0 )
            encodedByte = encodedByte | 128;
        mqtt_txbuf[mqtt_txlen++] = encodedByte;
    }while ( DataLen > 0 );    
    
    //可变报头
    mqtt_txbuf[mqtt_txlen++] = 0;            //消息标识符 MSB
    mqtt_txbuf[mqtt_txlen++] = 0x01;        //消息标识符 LSB
    //有效载荷
    mqtt_txbuf[mqtt_txlen++] = BYTE1(topiclen);//主题长度 MSB
    mqtt_txbuf[mqtt_txlen++] = BYTE0(topiclen);//主题长度 LSB   
    memcpy(&mqtt_txbuf[mqtt_txlen],topic,topiclen);
    mqtt_txlen += topiclen;
    
    if(whether)
    {
       mqtt_txbuf[mqtt_txlen++] = qos;//QoS级别
    }
    
//    for(i=0;i<10;i++)
//    {
        memset(mqtt_rxbuf,0,mqtt_rxlen);
        mqtt_send_data(mqtt_txbuf,mqtt_txlen);

        for(j=0;j<10;j++)
        {
            delay_ms(50);
            if (esp8266_wait_receive() == ESP8266_EOK)
                esp8266_copy_rxdata((char *)mqtt_rxbuf);

            if(mqtt_rxbuf[0]==parket_subAck[0] && mqtt_rxbuf[1]==parket_subAck[1]) //订阅成功               
            {
                return 0;//订阅成功
            }
        }
//    }
    return 1; //失败
}

//MQTT发布数据打包函数
//topic   主题 
//message 消息
//qos     消息等级 
uint8_t mqtt_publish_data(char *topic, char *message, uint8_t qos)
{  
    int topicLength = strlen(topic);    
    int messageLength = strlen(message);     
    static uint16_t id=0;
    int DataLen;
    mqtt_txlen=0;
    //有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度
    //QOS为0时没有标识符
    //数据长度             主题名   报文标识符   有效载荷
    if(qos)    DataLen = (2+topicLength) + 2 + messageLength;       
    else    DataLen = (2+topicLength) + messageLength;   

    //固定报头
    //控制报文类型
    mqtt_txbuf[mqtt_txlen++] = 0x30;    // MQTT Message Type PUBLISH  

    //剩余长度
    do
    {
        uint8_t encodedByte = DataLen % 128;
        DataLen = DataLen / 128;
        // if there are more data to encode, set the top bit of this byte
        if ( DataLen > 0 )
            encodedByte = encodedByte | 128;
        mqtt_txbuf[mqtt_txlen++] = encodedByte;
    }while ( DataLen > 0 );    
    
    mqtt_txbuf[mqtt_txlen++] = BYTE1(topicLength);//主题长度MSB
    mqtt_txbuf[mqtt_txlen++] = BYTE0(topicLength);//主题长度LSB 
    memcpy(&mqtt_txbuf[mqtt_txlen],topic,topicLength);//拷贝主题
    mqtt_txlen += topicLength;
        
    //报文标识符
    if(qos)
    {
        mqtt_txbuf[mqtt_txlen++] = BYTE1(id);
        mqtt_txbuf[mqtt_txlen++] = BYTE0(id);
        id++;
    }
    memcpy(&mqtt_txbuf[mqtt_txlen],message,messageLength);
    mqtt_txlen += messageLength;

//    int i = 0;
//    for(i=0;i<mqtt_txlen;i++)
//        printf("%02X ", mqtt_txbuf[i]);
//    printf("\r\n");
    mqtt_send_data(mqtt_txbuf,mqtt_txlen);
    return mqtt_txlen;
}

uint8_t mqtt_receive_handle(uint8_t *data_received, Mqtt_RxData_Type *rx_data)
{
    uint8_t *p;
    uint8_t encodeByte = 0;
    uint32_t multiplier = 1, Remaining_len = 0;
    uint8_t QS_level = 0;
    
    p = data_received;
    memset(rx_data, 0, sizeof(Mqtt_RxData_Type));
    
    //解析接收数据
    if((*p != 0x30)&&(*p != 0x32)&&(*p != 0x34))   //不是发布报文头
        return 1;
    
    if(*p != 0x30) QS_level = 1;    //标记qs等级不为0
    
    p++;
    //提取剩余数据长度
    do{
        encodeByte = *p++;
        Remaining_len += (encodeByte & 0x7F) * multiplier;
        multiplier *= 128;
        
        if(multiplier > 128*128*128) //超出剩余长度最大4个字节的要求,错误
            return 2;
    }while((encodeByte & 0x80) != 0);
    
    //提取主题数据长度
    rx_data->topic_len = *p++;
    rx_data->topic_len = rx_data->topic_len * 256 + *p++;
    //提取主题
    memcpy(rx_data->topic,p,rx_data->topic_len);
    p += rx_data->topic_len;
    
    if(QS_level != 0)  //跳过报文标识符
        p += 2;
    
    //提取payload
    rx_data->payload_len = Remaining_len - rx_data->topic_len - 2;
    memcpy(rx_data->payload, p, rx_data->payload_len);
    
//    printf("topic: %s\r\n", rx_data->topic);
//    printf("topic_len: %d\r\n", rx_data->topic_len);
//    printf("payload: %s\r\n", rx_data->payload);
//    printf("payload_len: %d\r\n", rx_data->payload_len);

    return 0;
}

void mqtt_send_response(uint8_t *id)
{
    char buf[128] = {0};
    sprintf(buf,"{\"id\":\"%s\",\"code\":200,\"msg\":\"success\"}",id);
    
    mqtt_publish_data(RELY_PUBLISH_TOPIC,(char *)buf,0);
    
    printf("\r\n发布数据:\r\n");
    printf((const char *)buf);    //发布的数据打印出来
    printf("\r\n");
}

void mqtt_send_heart(void)
{
    mqtt_send_data((uint8_t *)parket_heart,sizeof(parket_heart));
}

void mqtt_disconnect(void)
{
    mqtt_send_data((uint8_t *)parket_disconnet,sizeof(parket_disconnet));
}

void mqtt_send_data(uint8_t *buf,uint16_t len)
{
    esp8266_send_data((char *)buf, len);
}

onenet.h

#ifndef _ONENET_H_
#define _ONENET_H_

#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "stdarg.h"
#include "delay.h"

#define BYTE0(dwTemp)       (*( char *)(&dwTemp))
#define BYTE1(dwTemp)       (*((char *)(&dwTemp) + 1))
#define BYTE2(dwTemp)       (*((char *)(&dwTemp) + 2))
#define BYTE3(dwTemp)       (*((char *)(&dwTemp) + 3))
    
extern char MQTT_ClientID[100]; //MQTT_客户端ID
extern char MQTT_UserName[100]; //MQTT_用户名
extern char MQTT_PassWord[200]; //MQTT_密码

typedef struct
{
    uint8_t topic[512];
    uint16_t topic_len;
    uint8_t payload[512];
    uint16_t payload_len;
} Mqtt_RxData_Type;

//云服务器的设备证书
#define PRODUCT_KEY "0t8qlhHwDA"
#define DEVICE_NAME "myhome"
#define DEVICE_SECRET "6IoPlYQEsVXCykOUJckvoCjQq%2BM%3D"

//订阅与发布的主题
#define RELY_PUBLISH_TOPIC  "$sys/0t8qlhHwDA/myhome/thing/property/set_reply"  //属性设置应答订阅主题,onenet studio定义好的
#define SET_TOPIC  "$sys/0t8qlhHwDA/myhome/thing/property/set"
#define POST_TOPIC "$sys/0t8qlhHwDA/myhome/thing/property/post"
//事件上报主题
#define EVENT_PUBLISH_TOPIC   "$sys/0t8qlhHwDA/myhome/thing/event/post"  //发布主题,onenet studio定义好的

//阿里云用户名初始化
void mqtt_login_init(char *ProductKey,char *DeviceName,char *DeviceSecret);
//MQTT协议相关函数声明
uint8_t mqtt_publish_data(char *topic, char *message, uint8_t qos);
uint8_t mqtt_subscribe_topic(char *topic,uint8_t qos,uint8_t whether);
void mqtt_init(void);
uint8_t mqtt_connect(char *ClientID,char *Username,char *Password);
void mqtt_send_heart(void);
void mqtt_disconnect(void);
void mqtt_send_data(uint8_t *buf,uint16_t len);
void mqtt_send_response(uint8_t *id);
uint8_t mqtt_receive_handle(uint8_t *data_received, Mqtt_RxData_Type *rx_data);
#endif

六、实物图

在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐