基于FreeRtos物联网的饮水系统

2 篇文章 0 订阅
1 篇文章 0 订阅

目录

前言:

一、物联网赛题

1.应用场景描述

2. 竞赛题目

A. 直饮水机

B. 后端服务器

C. 前端应用终端

D. 巡检装置

二、stm32端使用FreeRtos

1、任务的创建

2、按键与按键矩阵扫描函数

3、Lcd初始化

4、Oled初始化

5、RTC初始化以及时间的设置

6、定时器PWM,编码器捕获,和基本定时器配置

7、FLash读写操作

8、ADC初始化,DMA传输

9、5个串口初始化

10、esp8266wifi连接与断开

11、stm32端最终效果图

三、后台服务器(linux下QT编写)

1、登录界面代码

2、TCP初始化

3、数据库的打开或创建

4、最终效果如图

四、前端应用终端(QT安卓开发)

1、主要代码如下

2、最终效果如图

五、巡检装置(QT安卓开发)

1、主要代码如下

2、效果如图


前言:

本系统为2022湖南省物联网应用创新竞赛(技能赛)赛题,本项目用到的模块和设备有:

正点原子的战舰板                        用做整个系统的饮水机

电脑虚拟机Ubuntu                        作为后端服务器

手机客户端app                              作为前端应用终端

手机蓝牙app                                  作为巡检装置

Lcd                                                显示相关信息和数据

Oled                                              显示相关信息和数据

gy39                                              采集温度,湿度,光照等

ADC                                              采集电压

两个编码器电机                             一个用于水泵加水,一个用于模拟饮水机出水

HC-05                                            与手机蓝牙app通信

esp8266                                        wifi模块,与服务器通信

Beep                                              故障和警告的提示

led                                                  指示灯

TIME                                               PWM输出,编码器捕获,定时

按键                                                执行各项任务

LTC2944                                        采集电池电压,电流

FLASH                                           断网重传,断电重载

RTC                                               设置并用作时间

串口                                                数据传输

一、物联网赛题

1.应用场景描述

直饮水在校园和公共场所到处可见,如校园教室楼、图书馆、体育馆、食堂和宿舍,以及公共场所如火车站、机场、购物中心、公园等。直饮水给我们学习、工作和生活带来很多便利。然而在直饮水广泛使用的同时,我们也面临如下问题:

        1、到了一个陌生地方,如何知道哪里有水喝?
        2、所供饮水设备是否工作正常?水质是否合格?
        3、作为直饮水管理部门,如何快速、准确、方便了解所管控设备的状况?
        基于物联网的校园直饮水管理系统让饮水、饮水人、饮水机、饮水管理和关注者等所有与“饮水”关联的人和物之间实现饮水信息相连相息。

2. 竞赛题目

        不限平台,搭建基于物联网的校园直饮水管理系统。假设系统由直饮水机、后端服务器、前端应用终端、以及直饮水机专用巡检装置组成,各部分功能作用如下:

A. 直饮水机

设直饮水机组成和控制如图所示:


1) 每台饮水机有一个唯一的ID号, 如:ID1=2001 ID2=2002等;。

2) 饮水机可以读取学生校园卡 ( RFID卡,数量2个及以上)卡号CardNumber,只有当合法学生校园卡放置在读卡区(比赛场景仅要求读面到校园卡号,无需核实其合法性),饮水机才提供饮水服务;

3) 每台饮水机有“复位”、“暂停”“正常”三种状态,其工作能被远皮远程后台控制:

        a)饮水机在 “复位”、“暂停”状态时不提供饮水服务,仅“正常状态提供饮水服务;

        b) 饮水机每次上电后进入“复位"并向后台发送复位消息。后台收到复位消息后,自动执行:

                i. 向饮水机发送系统时钟(时分秒),以帮助饮水机同步时间(饮水机后续计时可由本地处理);

                ii. 根据系统设定情况向饮水机发出“正常”或“暂停”指令。

4) 常温水箱内部设有高水位(Wh)、 低水位(W1)检测传感器(竞赛场景可用接近开关、红外开关、微动开关、或按键等代替):

        a). “正常”状态时,当水位低于Wl时自动启动加压水泵M (竞赛中用电机代替),经M加压的高压水通过净化装置净化后流入常温水箱;当水位高于Wh时加压泵M停止工作。

        b). 如果水位传感器状态指示:低于WI、同时高于Wh,判断为设备不正常,系统应提示设备故障(现场显示故障,蜂鸣器循环发“响0.4秒、停0.6秒”报警声,并向后台发出设备故障代码“Error1”);

5) TDS ( 水中固体物质含量指标,单位ppm)是衡量水质的一一个重要指标。TDS测定与测量探头设计、测量电路、被测水温、标定方法等因素相关,具体到水质测量一般可简化为水温和电压的测量。例如:

TDS=110*(Vcc-V)/V)*(1+0.02*(T-25))
1
是一款水质传感器的TDS计算公式。假设在水源(净化前)和常温水箱(净化后)两处测定TDS以监测饮水机工作状况和饮水水质情况,并假设被测水温T=25°C、水质测量电路供 电电压Vcc=5 (伏):
        a). 水质计算公 式为:

        水源水质: TDS1=110*((Vcc-V1)/V1)(V1不等于0时)

        饮水水质: TDS2=110*(Vcc-V2)/V2) (V2 不等于0时)
式中: V1、V2为水源和常温水箱两处水质探测得到的电压(竞赛场景用电压代替)。

        b)当水质TDS2值大于100ppm表示饮水水质不合格(现场显示警告,蜂鸣器循环发“响0.1秒、停0.9秒”警告声,并向后台发出警告代码“Warning1");

6) 饮水量由流量计F (竞赛场景可用编码器、按键等代替,每一个转动量、或脉冲计数代表一定流量饮水)测定;

7) 热饮水加热器P (可用指示灯代替)受热饮水温度T2自动控制:
a) 供热饮水时,当T2低于设定值TH时加热、高于TH时停止加热。
b)TH可本地或经移动终端尚设定和改变。

8) 电磁水阀s1或s2 (竞赛中电磁水阀可用指示灯代替,设常温饮水和热饮水共用一个出水口,控制常温饮水或热饮水开闭,S1和S2不能同时“开”(即不能同时出常温水和热)。

9) 按键K1 (常温水)和K2 (热水)操控饮水机出水
        a)停止出水(S1闭、S2闭)情况下:

                i. 按下K1出常温水,松开K1维持出常温水状态不变:
                ii. 或按下K2出热水,松开K2维持出热水状态不变:

        b) 出常温水(S1开、S2闭)时:

                i. 按下K1停止出水;
                ii. 或按下K2转换成出热水;

        c) 出热水(S1闭、S2开)时:

                i. 按下K1转换成出常温水;
                ii. 或按下K2停止出水;

        d) 一次出水量(流量计F计数值)超过一定流量值时,也自动停止出水。

10) 饮水机上能够显示(同时、分时、切换均可):

        a) 本地时间(时分秒)

        b) 水温(T1、T2)

        c) 水质(TDS1、 TDS2)

11) 饮水机具备与后台服务器通信功能,将下列信息传到后台服务器供管理和应用:
        a) 饮水机ID;
        b) 饮水学生校园卡卡号(CardNumber);
        c)饮水时间(时分秒)
        d)饮水量
        e)故障时设备故障代码“Error1”
        f)警告时警告代码“Warning1”。

12) 挑战性功能1:当饮水机临时断电,本地或经移动终端设置的热水控制参数TH,在饮水机复电后仍然自动有效;

13) 挑战性功能2:当饮水机与后台服务器“断网”,联网后能够将“断网”期间发生的相关饮水事件数据不丢失地传送至后端服务器。竞赛申假设“断网”期间发生的饮水事件不小于2次。

B. 后端服务器

        构建校园直饮水信息物联网后台服务器,支撑校园直饮水信息收集、储存、管理、应用。竞赛题目要求后台服务器:
        1) 与各饮水机之间建立通信联系;

        2) 收到某饮水机“复位”消息后,向饮水机回送系统时钟(时分秒) ,系统设定情况向饮水机发出正常”或“暂停”指令(参见A3.b)

        3) 后台服务器数据或数据库包含:

                a)饮水机位置信息, 如:

                        1D=2001位置:图书馆、或其它
                        1D-2002位置:教学楼、或其它

                b)饮水机工作属性:

                        复位:(上电未与服务器连通时状态、不提供饮水服务)
                        暂停: (该饮水机因故暂停工作、不提供饮水服务)
                        正常: (该饮水机正常工作)

        4) 接收、记录饮水机发出的各种饮水信息(参见A.11);

        5) 给应用前端(移动端用户、计算机用户等)提供查询、统计和控制操作等。

C. 前端应用终端

        校园直饮水信息系统前端应用如通过网络终端(如计算机)或移动终端(如智能手机App)提供的应用(竞赛作品简化,不区分饮水用户和管理用户),要求:

        1)选择实现:移动终端(智能手机App)或网络终端(网页浏览器),二选一即可,移动终端优先;

        2)应用终端上可修改后台服务器上饮水机工作属性(“正常”或“暂停”)。修改后,后台服务器应根据工作属性对饮水机进行同步控制;

        3)应用终端上可查询全部饮水机(不少于2台)位置、工作状态及水质信息;

        4)应用终端上可查询某饮水机某段时间内输出的饮水总量;

        5)应用终端上可查询某学生某段时间内在所有饮水机(不少于2台)上的饮水总量;6)饮水机“故障”、或“报警”时,应用终端上同步显示。

D. 巡检装置

        专用于饮水机维护维修的移动装置。当巡检人员携带该巡检装置到达饮水机附近时,可与饮水机进行信息交互,方便维护维修人员快速了解饮水机状况,要求:

        1)与饮水机之间不经过后台服务器、公共通信网络,仅巡检装置与饮水机之间现场点对点、无线方式通信;

        2)巡检设备可显示连接的饮水机: ID 号,水质检测电压V1、V2,报警信息。

二、stm32端使用FreeRtos

1、任务的创建
//TASK1 任务 配置
#define TASK1_PRIO      1                   /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
void task1(void *pvParameters);             /* 任务函数 */

/* 创建任务1 */
xTaskCreate(task1,"task1",TASK1_STK_SIZE,NULL,TASK1_PRIO,&Task1Task_Handler);


//任务1
void task1(void *pvParameters)
{
    while(1)
    {
        //printf("1\r\n");
        vTaskDelay(500);/* 延时1000ticks 0.5s*/
    }
}
2、按键与按键矩阵扫描函数
uint8_t key_scan(uint8_t mode)
{
    static uint8_t key_up = 1;  /* 按键按松开标志 */
    uint8_t keyval = 0;
    uint8_t k2_kup=0;

    if (mode) key_up = 1;       /* 支持连按 */

    if (key_up && (KEY0 == 0 || KEY1 == 0 || KEY2 == 0 || WK_UP == 1))  /* 按键松开标志为1, 且有任意一个按键按下了 */
    {
        delay_ms(10);           /* 去抖动 */
        key_up = 0;

        if (KEY0 == 0)  keyval = KEY0_PRES;

        if (KEY1 == 0)  keyval = KEY1_PRES;

        if (KEY2 == 0)  
        {
            keyval = KEY2_PRES;
            k2_kup=1;
        }
        if (WK_UP == 1) 
        {
            keyval = WKUP_PRES;
            if(k2_kup==1)
                k2_kup=2;
        }
        if(mode)
            vTaskDelay(100);
    }
    else if (KEY0 == 1 && KEY1 == 1 && KEY2 == 1 && WK_UP == 0) /* 没有任何按键按下, 标记按键松开 */
    {
        key_up = 1;
    }
    if(k2_kup==2)
        return K2_KUP;
    else
        return keyval;              /* 返回键值 */
}

uint8_t matrix_key_scan(void)
{
    uint8_t keyval = 0;
    
    
    //按键矩阵
    //输出引脚R1输出高电平
    KEYR1(1);
    KEYR2(0);
    vTaskDelay(10);
    if (KEYC1==0 || KEYC2==0 || KEYC3==0 || KEYC4==0)  /* 按键松开标志为1, 且有任意一个按键按下了 */
    {
        vTaskDelay(10);           /* 去抖动 */
        
        if(KEYC1 == 0) keyval=KEYS1_PRES;
        if(KEYC2 == 0) keyval=KEYS2_PRES;
        if(KEYC3 == 0) keyval=KEYS3_PRES;
        if(KEYC4 == 0) keyval=KEYS4_PRES;
        vTaskDelay(300);           /* 减少连按频率 */
    }
    KEYR1(0);
    KEYR2(1);
    vTaskDelay(10);
    if (KEYC1==0 || KEYC2==0 || KEYC3==0 || KEYC4==0)  /* 按键松开标志为1, 且有任意一个按键按下了 */
    {
        vTaskDelay(10);           /* 去抖动 */
        
        if(KEYC1 == 0) keyval=KEYS5_PRES;
        if(KEYC2 == 0) keyval=KEYS6_PRES;
        if(KEYC3 == 0) keyval=KEYS7_PRES;
        if(KEYC4 == 0) keyval=KEYS8_PRES;
        vTaskDelay(300);           /* 减少连按频率 */
    }
    return keyval;              /* 返回键值 */
}
3、Lcd初始化
void lcd_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    FSMC_NORSRAM_TimingTypeDef fsmc_read_handle;
    FSMC_NORSRAM_TimingTypeDef fsmc_write_handle;

    LCD_CS_GPIO_CLK_ENABLE();   /* LCD_CS脚时钟使能 */
    LCD_WR_GPIO_CLK_ENABLE();   /* LCD_WR脚时钟使能 */
    LCD_RD_GPIO_CLK_ENABLE();   /* LCD_RD脚时钟使能 */
    LCD_RS_GPIO_CLK_ENABLE();   /* LCD_RS脚时钟使能 */
    LCD_BL_GPIO_CLK_ENABLE();   /* LCD_BL脚时钟使能 */
    
    gpio_init_struct.Pin = LCD_CS_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;                /* 推挽复用 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */
    HAL_GPIO_Init(LCD_CS_GPIO_PORT, &gpio_init_struct);     /* 初始化LCD_CS引脚 */

    gpio_init_struct.Pin = LCD_WR_GPIO_PIN;
    HAL_GPIO_Init(LCD_WR_GPIO_PORT, &gpio_init_struct);     /* 初始化LCD_WR引脚 */

    gpio_init_struct.Pin = LCD_RD_GPIO_PIN;
    HAL_GPIO_Init(LCD_RD_GPIO_PORT, &gpio_init_struct);     /* 初始化LCD_RD引脚 */

    gpio_init_struct.Pin = LCD_RS_GPIO_PIN;
    HAL_GPIO_Init(LCD_RS_GPIO_PORT, &gpio_init_struct);     /* 初始化LCD_RS引脚 */

    gpio_init_struct.Pin = LCD_BL_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
    HAL_GPIO_Init(LCD_BL_GPIO_PORT, &gpio_init_struct);     /* LCD_BL引脚模式设置(推挽输出) */

    g_sram_handle.Instance = FSMC_NORSRAM_DEVICE;
    g_sram_handle.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
    
    g_sram_handle.Init.NSBank = FSMC_NORSRAM_BANK4;                        /* 使用NE4 */
    g_sram_handle.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;     /* 地址/数据线不复用 */
    g_sram_handle.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;    /* 16位数据宽度 */
    g_sram_handle.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;   /* 是否使能突发访问,仅对同步突发存储器有效,此处未用到 */
    g_sram_handle.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW; /* 等待信号的极性,仅在突发模式访问下有用 */
    g_sram_handle.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;      /* 存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT */
    g_sram_handle.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;       /* 存储器写使能 */
    g_sram_handle.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;              /* 等待使能位,此处未用到 */
    g_sram_handle.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE;           /* 读写使用不同的时序 */
    g_sram_handle.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;  /* 是否使能同步传输模式下的等待信号,此处未用到 */
    g_sram_handle.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;              /* 禁止突发写 */
    

    /* FSMC读时序控制寄存器 */
    fsmc_read_handle.AddressSetupTime = 0;      /* 地址建立时间(ADDSET)为1个HCLK 1/72M = 13.9ns */
    fsmc_read_handle.AddressHoldTime = 0;       /* 地址保持时间(ADDHLD) 模式A是没有用到 */
    /* 因为液晶驱动IC的读数据的时候,速度不能太快,尤其是个别奇葩芯片 */
    fsmc_read_handle.DataSetupTime = 15;        /* 数据保存时间(DATAST)为16个HCLK = 13.9 * 16 = 222.4ns */
    fsmc_read_handle.AccessMode = FSMC_ACCESS_MODE_A;     /* 模式A */
    
    /* FSMC写时序控制寄存器 */
    fsmc_write_handle.AddressSetupTime = 0;     /* 地址建立时间(ADDSET)为1个HCLK = 13.9ns */
    fsmc_write_handle.AddressHoldTime = 0;      /* 地址保持时间(ADDHLD) 模式A是没有用到 */
    fsmc_write_handle.DataSetupTime = 1;        /* 数据保存时间(DATAST)为2个HCLK = 13.9 * 2 = 27.8ns */
    /* 某些液晶驱动IC的写信号脉宽,最少也得50ns。 */
    fsmc_write_handle.AccessMode = FSMC_ACCESS_MODE_A;    /* 模式A */
    
    HAL_SRAM_Init(&g_sram_handle, &fsmc_read_handle, &fsmc_write_handle);
    delay_ms(50);

    /* 尝试9341 ID的读取 */
    lcd_wr_regno(0XD3);
    lcddev.id = lcd_rd_data();  /* dummy read */
    lcddev.id = lcd_rd_data();  /* 读到0X00 */
    lcddev.id = lcd_rd_data();  /* 读取93 */
    lcddev.id <<= 8;
    lcddev.id |= lcd_rd_data(); /* 读取41 */

    if (lcddev.id != 0X9341)    /* 不是 9341 , 尝试看看是不是 ST7789 */
    {
        lcd_wr_regno(0X04);
        lcddev.id = lcd_rd_data();      /* dummy read */
        lcddev.id = lcd_rd_data();      /* 读到0X85 */
        lcddev.id = lcd_rd_data();      /* 读取0X85 */
        lcddev.id <<= 8;
        lcddev.id |= lcd_rd_data();     /* 读取0X52 */
        
        if (lcddev.id == 0X8552)        /* 将8552的ID转换成7789 */
        {
            lcddev.id = 0x7789;
        }

        if (lcddev.id != 0x7789)        /* 也不是ST7789, 尝试是不是 NT35310 */
        {
            lcd_wr_regno(0XD4);
            lcddev.id = lcd_rd_data();  /* dummy read */
            lcddev.id = lcd_rd_data();  /* 读回0X01 */
            lcddev.id = lcd_rd_data();  /* 读回0X53 */
            lcddev.id <<= 8;
            lcddev.id |= lcd_rd_data(); /* 这里读回0X10 */

            if (lcddev.id != 0X5310)    /* 也不是NT35310,尝试看看是不是NT35510 */
            {
                /* 发送秘钥(厂家提供,照搬即可) */
                lcd_write_reg(0xF000, 0x0055);
                lcd_write_reg(0xF001, 0x00AA);
                lcd_write_reg(0xF002, 0x0052);
                lcd_write_reg(0xF003, 0x0008);
                lcd_write_reg(0xF004, 0x0001);
                
                lcd_wr_regno(0xC500);           /* 读取ID高8位 */
                lcddev.id = lcd_rd_data();      /* 读回0X55 */
                lcddev.id <<= 8;

                lcd_wr_regno(0xC501);           /* 读取ID低8位 */
                lcddev.id |= lcd_rd_data();     /* 读回0X10 */

                if (lcddev.id != 0X5510)        /* 也不是NT5510,尝试看看是不是SSD1963 */
                {
                    lcd_wr_regno(0XA1);
                    lcddev.id = lcd_rd_data();
                    lcddev.id = lcd_rd_data();  /* 读回0X57 */
                    lcddev.id <<= 8;
                    lcddev.id |= lcd_rd_data(); /* 读回0X61 */

                    if (lcddev.id == 0X5761)lcddev.id = 0X1963; /* SSD1963读回的ID是5761H,为方便区分,我们强制设置为1963 */
                }
            }
        }
    }

    /* 特别注意, 如果在main函数里面屏蔽串口1初始化, 则会卡死在printf
     * 里面(卡死在f_putc函数), 所以, 必须初始化串口1, 或者屏蔽掉下面
     * 这行 printf 语句 !!!!!!!
     */
    printf("LCD ID:%x\r\n", lcddev.id); /* 打印LCD ID */

    if (lcddev.id == 0X7789)
    {
        lcd_ex_st7789_reginit();    /* 执行ST7789初始化 */
    }
    else if (lcddev.id == 0X9341)
    {
        lcd_ex_ili9341_reginit();   /* 执行ILI9341初始化 */
    }
    else if (lcddev.id == 0x5310)
    {
        lcd_ex_nt35310_reginit();   /* 执行NT35310初始化 */
    }
    else if (lcddev.id == 0x5510)
    {
        lcd_ex_nt35510_reginit();   /* 执行NT35510初始化 */
    }
    else if (lcddev.id == 0X1963)
    {
        lcd_ex_ssd1963_reginit();   /* 执行SSD1963初始化 */
        lcd_ssd_backlight_set(100); /* 背光设置为最亮 */
    }

    lcd_display_dir(0); /* 默认为竖屏 */
    LCD_BL(1);          /* 点亮背光 */
    lcd_clear(WHITE);
}
4、Oled初始化
//oled初始化
void oled_init(void)
{
    oled_i2c_init();//i2c初始化
    delay_ms(100);//延时,重要
    
    oled_write_cmd(0xAE);   /* 关闭显示 */
    oled_write_cmd(0xD5);   /* 设置时钟分频因子,震荡频率 */
    oled_write_cmd(0xf0);     /* [3:0],分频因子;[7:4],震荡频率 */
    oled_write_cmd(0xA8);   /* 设置驱动路数 */
    oled_write_cmd(0X3F);   /* 默认0X3F(1/64) */
    oled_write_cmd(0xD3);   /* 设置显示偏移 */
    oled_write_cmd(0X00);   /* 设置列(低4bit)*/
    oled_write_cmd(0x10);//设置列(高4bit)

    oled_write_cmd(0x40);   /* 设置显示开始行 [5:0],行数. */

    oled_write_cmd(0x8D);   /* 电荷泵设置 */
    oled_write_cmd(0x14);   /* bit2,开启/关闭 */
    oled_write_cmd(0x20);   /* 设置内存地址模式 */
    oled_write_cmd(0x02);   /* [1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10; */
    oled_write_cmd(0xb0);   //开启地址0-7
    oled_write_cmd(0xA1);   /* 段重定义设置,bit0:0,0->0;1,0->127; */
    oled_write_cmd(0xC8);   /* 设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数 */
    oled_write_cmd(0xDA);   /* 设置COM硬件引脚配置 */
    oled_write_cmd(0x12);   /* 128*64 */

    oled_write_cmd(0x81);   /* 对比度设置 */
    oled_write_cmd(0xEF);   /* 1~255;默认0X7F (亮度设置,越大越亮) */
    oled_write_cmd(0xD9);   /* 设置预充电周期 */
    oled_write_cmd(0x22);   //充电时间
    oled_write_cmd(0xf1);   /* [3:0],PHASE 1;[7:4],PHASE 2; */
    oled_write_cmd(0xDB);   /* 设置VCOMH 电压倍率 */
    oled_write_cmd(0x20);   //0x20,0.77xVcc

    oled_write_cmd(0xA4);   /* 全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏) */
    oled_write_cmd(0xA6);   /* 设置显示方式;bit0:1,反相显示;0,正常显示 */
    oled_write_cmd(0xAF);   /* 开启显示 */
    
    oled_display_off();
    oled_all_clear();
    oled_display_on();
}
5、RTC初始化以及时间的设置
//初始化
void rtc_init(void)
{
    //rtc初始化
    rtc_handle.Instance=RTC;
    rtc_handle.Init.AsynchPrediv=32767;//预分频值
    rtc_handle.Init.OutPut=RTC_OUTPUTSOURCE_NONE;
    HAL_RTC_Init(&rtc_handle);
    
    //开启LSE
    rcc_os.OscillatorType=RCC_OSCILLATORTYPE_LSE;//类型选为LSE
    rcc_os.LSEState=RCC_LSE_ON;//状态打开
    rcc_os.PLL.PLLState=RCC_PLL_NONE;
    HAL_RCC_OscConfig(&rcc_os);
    
    //选择时钟源
    rcc_perclk.PeriphClockSelection=RCC_PERIPHCLK_RTC;//扩展时钟选RTC
    rcc_perclk.RTCClockSelection=RCC_RTCCLKSOURCE_LSE;//时钟源
    HAL_RCCEx_PeriphCLKConfig(&rcc_perclk);
}

//Mspinit函数
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
    __HAL_RCC_PWR_CLK_ENABLE();//电源时钟
    __HAL_RCC_BKP_CLK_ENABLE();//备份域时钟
    HAL_PWR_EnableBkUpAccess();//使能备份域访问
    __HAL_RCC_RTC_ENABLE();//使能rtc
    
    //设置时间
    rtc_set_time(2023,9,23,10,47,0);
    
    //中断
    __HAL_RTC_ALARM_ENABLE_IT(&rtc_handle, RTC_IT_SEC);   /* 允许秒中断 */
    HAL_NVIC_SetPriority(RTC_IRQn,2,2);
    HAL_NVIC_EnableIRQ(RTC_IRQn);
}
6、定时器PWM,编码器捕获,和基本定时器配置
TIM_HandleTypeDef tim3_handle={0};
TIM_HandleTypeDef tim8_handle={0};
TIM_OC_InitTypeDef tim8_oc={0};
TIM_Encoder_InitTypeDef tim_encoder={0};
TIM_HandleTypeDef tim6_handle={0};

//定时器初始化
void time8_init(void)
{
    //定时器初始化
    tim8_handle.Instance=TIM8;
    tim8_handle.Init.Period=999;
    tim8_handle.Init.Prescaler=719;
    tim8_handle.Init.CounterMode=TIM_COUNTERMODE_UP;
    tim8_handle.Init.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE;//使能缓冲
    HAL_TIM_PWM_Init(&tim8_handle);
    
    //配置PWM
    tim8_oc.OCMode=TIM_OCMODE_PWM1;
    tim8_oc.OCPolarity=TIM_OCPOLARITY_HIGH;
    tim8_oc.Pulse=0;
    HAL_TIM_PWM_ConfigChannel(&tim8_handle,&tim8_oc,TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&tim8_handle,TIM_CHANNEL_1);//开启
    HAL_TIM_PWM_ConfigChannel(&tim8_handle,&tim8_oc,TIM_CHANNEL_2);
    HAL_TIM_PWM_Start(&tim8_handle,TIM_CHANNEL_2);//开启
}

void time3_init(void)
{
    //定时器3初始化
    tim3_handle.Instance=TIM3;
    tim3_handle.Init.Period=65535;
    tim3_handle.Init.Prescaler=0;
    tim3_handle.Init.CounterMode=TIM_COUNTERMODE_UP;
    tim3_handle.Init.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_DISABLE;//失能缓冲
    tim3_handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//不分频
    
    //配置编码器捕获
    tim_encoder.EncoderMode=TIM_ENCODERMODE_TI12;//四倍频模式
    tim_encoder.IC1Filter=0;//通道1滤波器
    tim_encoder.IC1Polarity=TIM_ICPOLARITY_RISING;//通道1捕获上升沿
    tim_encoder.IC1Prescaler=TIM_ICPSC_DIV1;//不分频
    tim_encoder.IC1Selection=TIM_ICSELECTION_DIRECTTI ;//TIM输入1、2、3或4被选择为分别连接到IC1、IC2、IC3或IC4
    tim_encoder.IC2Filter=0;
    tim_encoder.IC2Polarity=TIM_ICPOLARITY_RISING;//通道2捕获上升沿
    tim_encoder.IC2Prescaler=TIM_ICPSC_DIV1;//不分频
    tim_encoder.IC2Selection=TIM_ICSELECTION_DIRECTTI ;//TIM输入1、2、3或4被选择为分别连接到IC1、IC2、IC3或IC4
    HAL_TIM_Encoder_Init(&tim3_handle,&tim_encoder);
    
    __HAL_TIM_SET_COUNTER(&tim3_handle, 0);/* 清零计数器 */
    
    __HAL_TIM_CLEAR_IT(&tim3_handle,TIM_IT_UPDATE);/* 清零中断标志位 */
    __HAL_TIM_ENABLE_IT(&tim3_handle,TIM_IT_UPDATE);/* 使能定时器的更新事件中断 */

    //HAL_TIM_Encoder_Start(&tim3_handle, TIM_CHANNEL_ALL);//开始编码器计数
    HAL_TIM_Encoder_Start_IT(&tim3_handle, TIM_CHANNEL_ALL);//开始编码器计数,使能所有通道捕获中断
}

void time6_init(void)
{
    //定时器初始化
    tim6_handle.Instance=TIM6;
    tim6_handle.Init.Period=719;
    tim6_handle.Init.Prescaler=9999;
    tim6_handle.Init.CounterMode=TIM_COUNTERMODE_UP;
    tim6_handle.Init.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE;//使能缓冲
    HAL_TIM_Base_Init(&tim6_handle);//初始化
    HAL_TIM_Base_Start_IT(&tim6_handle);//启动定时器,使能定时器更新中断
}


//基本定时器MspInit
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance==TIM6)
    {
        __HAL_RCC_TIM6_CLK_ENABLE();//tim8时钟使能
        
        HAL_NVIC_SetPriority(TIM6_IRQn,4,0);//中断优先级
        HAL_NVIC_EnableIRQ(TIM6_IRQn);//使能中断
    }
}
//编码器模式Mspinit函数
void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef *htim)
{
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_TIM3_CLK_ENABLE();
    
    GPIO_InitTypeDef gpio_init;
    gpio_init.Pin=GPIO_PIN_6|GPIO_PIN_7;
    gpio_init.Mode=GPIO_MODE_INPUT;
    gpio_init.Pull=GPIO_PULLUP;
    gpio_init.Speed=GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA,&gpio_init);//TIM3 GPIO
    
    //设置中断
    HAL_NVIC_SetPriority(TIM3_IRQn,2,2);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
}

// 输出PWM模式模式Mspinit函数
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_TIM8_CLK_ENABLE();
    
    GPIO_InitTypeDef gpio_init;
    gpio_init.Pin=GPIO_PIN_6 | GPIO_PIN_7;
    gpio_init.Mode=GPIO_MODE_AF_PP;
    gpio_init.Pull=GPIO_PULLUP;
    gpio_init.Speed=GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOC,&gpio_init);
}
7、FLash读写操作
void stmflash_read(uint32_t raddr, uint16_t *pbuf, uint16_t length)
{
    uint16_t i;

    for (i = 0; i < length; i++)
    {
        pbuf[i] = stmflash_read_halfword(raddr);    /* 读取2个字节 */
        raddr += 2; /* 偏移2个字节 */
    }
}

uint16_t g_flashbuf[STM32_SECTOR_SIZE / 2]; /* 最多是2K字节 */
void stmflash_write(uint32_t waddr, uint16_t *pbuf, uint16_t length)
{
    uint32_t secpos;        /* 扇区地址 */
    uint16_t secoff;        /* 扇区内偏移地址(16位字计算) */
    uint16_t secremain;     /* 扇区内剩余地址(16位字计算) */
    uint16_t i;
    uint32_t offaddr;       /* 去掉0X08000000后的地址 */
    FLASH_EraseInitTypeDef flash_eraseop;
    uint32_t erase_addr;    /* 擦除错误,这个值为发生错误的扇区地址 */

    if (waddr < STM32_FLASH_BASE || (waddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
    {
        return;     /* 非法地址 */
    }

    HAL_FLASH_Unlock();     /* FLASH解锁 */

    offaddr = waddr - STM32_FLASH_BASE;             /* 实际偏移地址. */
    secpos = offaddr / STM32_SECTOR_SIZE;           /* 扇区地址  0~255 for STM32F103ZET6 */
    secoff = (offaddr % STM32_SECTOR_SIZE) / 2;     /* 在扇区内的偏移(2个字节为基本单位.) */
    secremain = STM32_SECTOR_SIZE / 2 - secoff;     /* 扇区剩余空间大小 */
    if (length <= secremain)
    {
        secremain = length; /* 不大于该扇区范围 */
    }

    while (1)
    {
        stmflash_read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE, g_flashbuf, STM32_SECTOR_SIZE / 2); /* 读出整个扇区的内容 */
        for (i = 0; i < secremain; i++)                                                              /* 校验数据 */
        {
            if (g_flashbuf[secoff + i] != 0XFFFF)
            {
                break;      /* 需要擦除 */
            }
        }
        if (i < secremain)  /* 需要擦除 */
        { 
            flash_eraseop.TypeErase = FLASH_TYPEERASE_PAGES;     /* 选择页擦除 */
            flash_eraseop.NbPages = 1;
            flash_eraseop.PageAddress = secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE;  /* 要擦除的扇区 */
            HAL_FLASHEx_Erase( &flash_eraseop, &erase_addr);

            for (i = 0; i < secremain; i++)         /* 复制 */
            {
                g_flashbuf[i + secoff] = pbuf[i];
            }
            stmflash_write_nocheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE, g_flashbuf, STM32_SECTOR_SIZE / 2); /* 写入整个扇区 */
        }
        else
        {
            stmflash_write_nocheck(waddr, pbuf, secremain); /* 写已经擦除了的,直接写入扇区剩余区间. */
        }
        if (length == secremain)
        {
            break; /* 写入结束了 */
        }
        else       /* 写入未结束 */
        {
            secpos++;               /* 扇区地址增1 */
            secoff = 0;             /* 偏移位置为0 */
            pbuf += secremain;      /* 指针偏移 */
            waddr += secremain * 2; /* 写地址偏移(16位数据地址,需要*2) */
            length -= secremain;    /* 字节(16位)数递减 */
            
            if (length > (STM32_SECTOR_SIZE / 2))
            {
                secremain = STM32_SECTOR_SIZE / 2; /* 下一个扇区还是写不完 */
            }
            else
            {
                secremain = length; /* 下一个扇区可以写完了 */
            }
        }
    }

    HAL_FLASH_Lock(); /* 上锁 */
}
8、ADC初始化,DMA传输
//ADC初始化
void adc_init(void)
{
    //1、ADC初始化
    adc_handle.Instance=ADC1;
    adc_handle.Init.ContinuousConvMode=ENABLE;             //多次转换模式
    adc_handle.Init.DataAlign=ADC_DATAALIGN_RIGHT;          //数据对齐方式,右对齐
    adc_handle.Init.DiscontinuousConvMode=DISABLE;          //间断模式,不使用
    adc_handle.Init.ExternalTrigConv=ADC_SOFTWARE_START;    //外部触发选择,软件触发
    adc_handle.Init.NbrOfConversion=1;                      //转换通道个数
    adc_handle.Init.NbrOfDiscConversion=0;                  //间断模式通道个数
    adc_handle.Init.ScanConvMode=ADC_SCAN_DISABLE;          //扫描模式,不扫描
    HAL_ADC_Init(&adc_handle);
    
    //2、校准
    HAL_ADCEx_Calibration_Start(&adc_handle);
    
    //3、配置时钟源,分频系数
    rcc_clk.AdcClockSelection=RCC_ADCPCLK2_DIV6;        //分频系数
    rcc_clk.PeriphClockSelection=RCC_PERIPHCLK_ADC;     //选择时钟
    HAL_RCCEx_PeriphCLKConfig(&rcc_clk);
    
    //4、配置相关参数
    adc_conf.Channel=ADC_CHANNEL_1;                     //通道
    adc_conf.Rank=ADC_REGULAR_RANK_1;                   //顺序,第一个
    adc_conf.SamplingTime=ADC_SAMPLETIME_239CYCLES_5;   //采样周期239
    HAL_ADC_ConfigChannel(&adc_handle,&adc_conf);
}

//Mspinit函数
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance==ADC1)
    {
        __HAL_RCC_ADC1_CLK_ENABLE();                           //adc1时钟使能
        __HAL_RCC_GPIOA_CLK_ENABLE();                          /* IO口PA时钟使能 */

        GPIO_InitTypeDef gpio_initstruct;
        gpio_initstruct.Pin = GPIO_PIN_1;                      /* LED0引脚 */
        gpio_initstruct.Mode = GPIO_MODE_ANALOG;               /* 模拟功能*/
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);                /* 初始化LED0引脚 */
    }
    
}

//使能一次ADC转换,DMA传输
void adc_dma_enabel(uint16_t cndtr)
{
    //先关闭ADC,DMA
    ADC1->CR2 &=~(1<<0);//关闭adc
    DMA1_Channel1->CCR &=~(1<<0);//关闭dma
    while(DMA1_Channel1->CCR & (1<<0));//等待关闭
    
    DMA1_Channel1->CNDTR=cndtr;//设置传输数量
    DMA1_Channel1->CCR |=(1<<0);//开启DMA}
    ADC1->CR2 |=(1<<0);//开启ADC
    ADC1->CR2 |=(1<<22);//开启规则转换
    
}
9、5个串口初始化
//串口1参数
/* 接收缓冲, 最大USART_REC_LEN个字节. */
uint8_t usart1_rex[RXBUFFERSIZE];  /* HAL库使用的串口接收缓冲 */
uint8_t usart1_rx_buf[USART_REC_LEN];  /* HAL库使用的串口接收缓冲 */
uint16_t usart1_rx_buf_len=0;
UART_HandleTypeDef g_uart1_handle;  /* UART句柄 */
//串口1初始化函数
void usart_init(uint32_t baudrate)
{
    /*UART 初始化设置*/
    g_uart1_handle.Instance = USART_UX;                                       /* USART_UX */
    g_uart1_handle.Init.BaudRate = baudrate;                                  /* 波特率 */
    g_uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;                      /* 字长为8位数据格式 */
    g_uart1_handle.Init.StopBits = UART_STOPBITS_1;                           /* 一个停止位 */
    g_uart1_handle.Init.Parity = UART_PARITY_NONE;                            /* 无奇偶校验位 */
    g_uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;                      /* 无硬件流控 */
    g_uart1_handle.Init.Mode = UART_MODE_TX_RX;                               /* 收发模式 */
    HAL_UART_Init(&g_uart1_handle);                                           /* HAL_UART_Init()会使能UART1 */

    /* 该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量 */
    HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)usart1_rex,RXBUFFERSIZE);
}

//串口2参数
uint8_t usart2_rex[RXBUFFERSIZE];
uint8_t Uart2RxBuf[30];
uint8_t Uart2RxDataConut = 0;
uint8_t Rx2Flag = 0;
UART_HandleTypeDef g_uart2_handle;       /* HAL UART2句柄 */
//串口2初始化函数
void usart2_init(uint32_t bound)
{
    g_uart2_handle.Instance=USART2;//串口基地址
    g_uart2_handle.Init.BaudRate=bound;//波特率
    g_uart2_handle.Init.WordLength=UART_WORDLENGTH_8B;//字长
    g_uart2_handle.Init.StopBits=UART_STOPBITS_1;//停止位长度
    g_uart2_handle.Init.Parity=UART_PARITY_NONE;//校验位
    g_uart2_handle.Init.HwFlowCtl=UART_HWCONTROL_NONE;//硬件流控
    g_uart2_handle.Init.Mode=UART_MODE_TX_RX;//收发
    HAL_UART_Init(&g_uart2_handle);//初始化串口
    
    HAL_UART_Receive_IT(&g_uart2_handle,(uint8_t*)usart2_rex,RXBUFFERSIZE);
}

//串口3参数
uint8_t usart3_rex[RXBUFFERSIZE];
uint8_t usart3_rx_buf[256];
uint16_t usart3_rx_buf_len = 0;
UART_HandleTypeDef g_uart3_handle;       /* HAL UART3句柄 */
uint8_t wifi_success=0;
//串口3初始化函数
void usart3_init(uint32_t bound)
{
    g_uart3_handle.Instance=USART3;//串口基地址
    g_uart3_handle.Init.BaudRate=bound;//波特率
    g_uart3_handle.Init.WordLength=UART_WORDLENGTH_8B;//字长
    g_uart3_handle.Init.StopBits=UART_STOPBITS_1;//停止位长度
    g_uart3_handle.Init.Parity=UART_PARITY_NONE;//校验位
    g_uart3_handle.Init.HwFlowCtl=UART_HWCONTROL_NONE;//硬件流控
    g_uart3_handle.Init.Mode=UART_MODE_TX_RX;//收发
    HAL_UART_Init(&g_uart3_handle);//初始化串口
    
    HAL_UART_Receive_IT(&g_uart3_handle,(uint8_t*)usart3_rex,RXBUFFERSIZE);
}

//串口4初始化
uint8_t uart4_rex[RXBUFFERSIZE];
uint8_t uart4_rx_buf[64];
uint16_t uart4_rx_buf_len = 0;
uint8_t hc05_rx_flag=0;
uint8_t hc05_rx_buf[64];
UART_HandleTypeDef g_uart4_handle;       /* HAL UART4句柄 */
void uart4_init(uint32_t bound)
{
    g_uart4_handle.Instance=UART4;//串口基地址
    g_uart4_handle.Init.BaudRate=bound;//波特率
    g_uart4_handle.Init.WordLength=UART_WORDLENGTH_8B;//字长
    g_uart4_handle.Init.StopBits=UART_STOPBITS_1;//停止位长度
    g_uart4_handle.Init.Parity=UART_PARITY_NONE;//校验位
    g_uart4_handle.Init.HwFlowCtl=UART_HWCONTROL_NONE;//硬件流控
    g_uart4_handle.Init.Mode=UART_MODE_TX_RX;//收发
    HAL_UART_Init(&g_uart4_handle);//初始化串口
    HAL_UART_Receive_IT(&g_uart4_handle,(uint8_t*)uart4_rex,RXBUFFERSIZE);
}

//串口5初始化
uint8_t uart5_rex[RXBUFFERSIZE];
uint8_t uart5_rx_buf[128];
uint16_t uart5_rx_buf_len = 0;
UART_HandleTypeDef g_uart5_handle;       /* HAL UART4句柄 */
void uart5_init(uint32_t bound)
{
    g_uart5_handle.Instance=UART5;//串口基地址
    g_uart5_handle.Init.BaudRate=bound;//波特率
    g_uart5_handle.Init.WordLength=UART_WORDLENGTH_8B;//字长
    g_uart5_handle.Init.StopBits=UART_STOPBITS_1;//停止位长度
    g_uart5_handle.Init.Parity=UART_PARITY_NONE;//校验位
    g_uart5_handle.Init.HwFlowCtl=UART_HWCONTROL_NONE;//硬件流控
    g_uart5_handle.Init.Mode=UART_MODE_TX_RX;//收发
    HAL_UART_Init(&g_uart5_handle);//初始化串口
    HAL_UART_Receive_IT(&g_uart5_handle,(uint8_t*)uart5_rex,RXBUFFERSIZE);
}

//UART底层初始化函数
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef gpio_init_struct;

    if (huart->Instance == USART_UX)                            /* 如果是串口1,进行串口1 MSP初始化 */
    {
        USART_TX_GPIO_CLK_ENABLE();                             /* 使能串口TX脚时钟 */
        USART_RX_GPIO_CLK_ENABLE();                             /* 使能串口RX脚时钟 */
        USART_UX_CLK_ENABLE();                                  /* 使能串口时钟 */

        gpio_init_struct.Pin = USART_TX_GPIO_PIN;               /* 串口发送引脚号 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* IO速度设置为高速 */
        HAL_GPIO_Init(USART_TX_GPIO_PORT, &gpio_init_struct);
                
        gpio_init_struct.Pin = USART_RX_GPIO_PIN;               /* 串口RX脚 模式设置 */
        gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;    
        HAL_GPIO_Init(USART_RX_GPIO_PORT, &gpio_init_struct);   /* 串口RX脚 必须设置成输入模式 */
        
#if USART_EN_RX
        HAL_NVIC_EnableIRQ(USART_UX_IRQn);                      /* 使能USART1中断通道 */
        HAL_NVIC_SetPriority(USART_UX_IRQn, 3, 3);              /* 组2,最低优先级:抢占优先级3,子优先级3 */
#endif
    }
    else if(huart->Instance==USART2)
    {
        //1、使能gpio和usart1
        __HAL_RCC_USART2_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        //2、初始化gpio
        gpio_init_struct.Pin=GPIO_PIN_2;
        gpio_init_struct.Mode=GPIO_MODE_AF_PP;//挽推复用
        gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOA,&gpio_init_struct);
        
        gpio_init_struct.Pin=GPIO_PIN_3;
        gpio_init_struct.Mode=GPIO_MODE_AF_INPUT;//输入
        gpio_init_struct.Pull=GPIO_PULLUP;//上拉
        gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOA,&gpio_init_struct);
        
        //设置优先级,使能中断
        HAL_NVIC_SetPriority(USART2_IRQn,3,3);
        HAL_NVIC_EnableIRQ(USART2_IRQn);
    }
    else if(huart->Instance==USART3)
    {
        //1、使能gpio和usart1
        __HAL_RCC_USART3_CLK_ENABLE();
        __HAL_RCC_GPIOB_CLK_ENABLE();
        
        //2、初始化gpio
        gpio_init_struct.Pin=GPIO_PIN_10;
        gpio_init_struct.Mode=GPIO_MODE_AF_PP;//挽推复用
        gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOB,&gpio_init_struct);
        
        gpio_init_struct.Pin=GPIO_PIN_11;
        gpio_init_struct.Mode=GPIO_MODE_AF_INPUT;//输入
        gpio_init_struct.Pull=GPIO_PULLUP;//上拉
        gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOB,&gpio_init_struct);
        
        //设置优先级,使能中断
        HAL_NVIC_SetPriority(USART3_IRQn,3,3);
        HAL_NVIC_EnableIRQ(USART3_IRQn);
    }
    else if(huart->Instance==UART4)
    {
         //1、使能gpio和usart1
        __HAL_RCC_UART4_CLK_ENABLE();
        __HAL_RCC_GPIOC_CLK_ENABLE();
        //2、初始化gpio
        gpio_init_struct.Pin=GPIO_PIN_10;
        gpio_init_struct.Mode=GPIO_MODE_AF_PP;//挽推复用
        gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOC,&gpio_init_struct);
        
        gpio_init_struct.Pin=GPIO_PIN_11;
        gpio_init_struct.Mode=GPIO_MODE_AF_INPUT;//输入
        gpio_init_struct.Pull=GPIO_PULLUP;//上拉
        gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOC,&gpio_init_struct);
        
        //设置优先级,使能中断
        HAL_NVIC_SetPriority(UART4_IRQn,3,3);
        HAL_NVIC_EnableIRQ(UART4_IRQn);
    }
    else if(huart->Instance==UART5)
    {
         //1、使能gpio和usart1
        __HAL_RCC_UART5_CLK_ENABLE();
        __HAL_RCC_GPIOC_CLK_ENABLE();
        __HAL_RCC_GPIOD_CLK_ENABLE();
        //2、初始化gpio
        gpio_init_struct.Pin=GPIO_PIN_12;
        gpio_init_struct.Mode=GPIO_MODE_AF_PP;//挽推复用
        gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOC,&gpio_init_struct);
        
        gpio_init_struct.Pin=GPIO_PIN_2;
        gpio_init_struct.Mode=GPIO_MODE_AF_INPUT;//输入
        gpio_init_struct.Pull=GPIO_PULLUP;//上拉
        gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOD,&gpio_init_struct);
        
        //设置优先级,使能中断
        HAL_NVIC_SetPriority(UART5_IRQn,3,3);
        HAL_NVIC_EnableIRQ(UART5_IRQn);
    }
}
10、esp8266wifi连接与断开
uint8_t AT_CMD[]="AT\r\n";//测试
uint8_t AT_REST[]="AT+RST\r\n";//复位
uint8_t AT_CWAUTOCONN[]="AT+CWAUTOCONN=0\r\n";//关闭自动连接
uint8_t AT_WIFI_CONNECT[]="AT+CWJAP_DEF=\"TP_226\",\"226226226\"\r\n";//连接wifi
uint8_t AT_CIPMUX[]="AT+CIPMUX=0\r\n";//配置单路连接
uint8_t AT_CWMODE[]="AT+CWMODE=1\r\n";//设置sta模式
uint8_t AT_TCP_CONNECT[]="AT+CIPSTART=\"TCP\",\"192.168.1.106\",6666\r\n";//TCP连接
uint8_t AT_CIPMODE[]="AT+CIPMODE=1\r\n";//打开透传
uint8_t AT_CIPSEND[]="AT+CIPSEND\r\n";//开始透传
uint8_t AT_EXIT[]="+++";//断开透传
uint8_t AT_CIPCLOSE[]="AT+CIPCLOSE\r\n";//关闭tcp连接

uint8_t open_send_flag=0;

//判断是否回应ok 1:OK,0:失败
uint8_t esp01s_ack_ok(void)
{
    uint8_t temp=0;
    while(!is_rt_buf_empty())//判断缓冲区是否有数据
    {
        temp=get_rt_buf();
        //printf("%c",temp);
        if(temp=='O')//判断是否为OK
        {
            temp=get_rt_buf();
            if(temp=='K')
            {
                printf("OK\r\n");
                r_flag=w_flag;
                return 0;
            }
        }
    }
    printf("erro\r\n");
    return 1;
}

void esp01s_read(void)
{
    uint8_t temp=0;
    while(!is_rt_buf_empty())//判断缓冲区是否有数据
    {
        temp=get_rt_buf();
        printf("%c",temp);
    }
}

//wifi配置 1:失败,0:成功
uint8_t wifi_config(uint8_t mode)//mode:0:任务外,1:任务内
{
//    HAL_UART_Transmit(&g_uart3_handle,AT_CMD,strlen((char *)AT_CMD),1000);
//    delay_ms(1000);
//    esp01s_ack_ok();
    //复位
    HAL_UART_Transmit(&g_uart3_handle,AT_REST,strlen((char *)AT_REST),1000);
    printf("复位:");
    if(!mode)
        delay_ms(1000);
    else
        vTaskDelay(1000);
    if(esp01s_ack_ok())
        return 1;
    else
    {
        wifi_success=0;
        open_send_flag=0;
    }
    
    //设置sta模式
    HAL_UART_Transmit(&g_uart3_handle,AT_CWMODE,strlen((char *)AT_CWMODE),1000);
    printf("设置sta模式");
    if(!mode)
        delay_ms(300);
    else
        vTaskDelay(300);
    if(esp01s_ack_ok())
        return 1;
    
    //连接wifi
    HAL_UART_Transmit(&g_uart3_handle,AT_WIFI_CONNECT,strlen((char *)AT_WIFI_CONNECT),1000);
    printf("连接wifi:");
    if(!mode)
        delay_ms(8000);
    else
        vTaskDelay(8000);
    if(esp01s_ack_ok())
        return 1;
    
    //设置单路连接
    HAL_UART_Transmit(&g_uart3_handle,AT_CIPMUX,strlen((char *)AT_CIPMUX),1000);
    printf("设置单路连接:");
    if(!mode)
        delay_ms(300);
    else
        vTaskDelay(300);
    if(esp01s_ack_ok())
        return 1;
    
    //打开透传
    HAL_UART_Transmit(&g_uart3_handle,AT_CIPMODE,strlen((char *)AT_CIPMODE),1000);
    printf("打开透传:");
    if(!mode)
        delay_ms(300);
    else
        vTaskDelay(300);
    if(esp01s_ack_ok())
        return 1;

    //连接TCP
    HAL_UART_Transmit(&g_uart3_handle,AT_TCP_CONNECT,strlen((char *)AT_TCP_CONNECT),1000);
    printf("连接TCP:");
    if(!mode)
        delay_ms(500);
    else
        vTaskDelay(500);
    if(esp01s_ack_ok())
        return 1;
    else
        wifi_success=1;
    
    return 0;
}

//开始透传
void wifi_open_cipsend(uint8_t mode)
{
    //wifi已近连接
    if(wifi_success && open_send_flag!=1)
    {
        //开始透传
        HAL_UART_Transmit(&g_uart3_handle,AT_CIPSEND,strlen((char *)AT_CIPSEND),500);
        printf("开始透传");
        if(!mode)
            delay_ms(300);
        else
            vTaskDelay(300);
        if(!esp01s_ack_ok())
            open_send_flag=1;
    }
}

//关闭透传
void wifi_close_cipsend(uint8_t mode)
{
    if(wifi_success && open_send_flag==1)
    {
        if(!mode)
            delay_ms(1000);
        else
            vTaskDelay(1000);
        //关闭透传
        HAL_UART_Transmit(&g_uart3_handle,AT_EXIT,strlen((char *)AT_EXIT),500);
        printf("关闭透传:");
        if(!mode)
            delay_ms(300);
        else
            vTaskDelay(300);
        HAL_UART_Transmit(&g_uart3_handle,AT_CMD,strlen((char *)AT_CMD),500);
        if(!mode)
            delay_ms(300);
        else
            vTaskDelay(300);
        if(!esp01s_ack_ok())
            open_send_flag=0;
    }
}

//关闭连接,返回0成功,1失败
uint8_t wifi_close_connect(uint8_t mode)
{
    //关闭透传
    HAL_UART_Transmit(&g_uart3_handle,AT_EXIT,strlen((char *)AT_EXIT),500);
    printf("关闭透传,断开连接:");
    if(!mode)
        delay_ms(1000);
    else
        vTaskDelay(1000);
    HAL_UART_Transmit(&g_uart3_handle,AT_CIPCLOSE,strlen((char *)AT_CIPCLOSE),1000);
    if(!mode)
        delay_ms(300);
    else
        vTaskDelay(300);
    open_send_flag=0;
    wifi_success=0;
    esp01s_read();
    return 0;
}
11、stm32端最终效果图

三、后台服务器(linux下QT编写)

1、登录界面代码
Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    //修改页面背景
    QPalette pal =  ui->stackedWidget->palette();
    pal.setBrush(QPalette::Background, QBrush(QImage("../picture/main.bmp")));
    ui->stackedWidget->setAutoFillBackground(true);
    ui->stackedWidget->setPalette(pal);

    ui->stackedWidget->setCurrentIndex(0);//跳转到登陆页面
    open_sql("../user.db");//打开或创建数据库

}

Dialog::~Dialog()
{
    delete ui;
}

//主窗口跳转到登陆窗口
void Dialog::m_return_login()
{
    this->show();
    open_sql("../user.db");//打开或创建数据库
}

//创建并打开数据库
void Dialog::open_sql(QString sql_path)
{
    userDb=QSqlDatabase::addDatabase("QSQLITE");//初始化连接对象,使用静态成员函数addDatabase,参数是数据库的类型
    userDb.setDatabaseName(sql_path);//设置数据库库名,建议带路径,如果数据库存在,则直接打开,如果数据不存在,则创建再打开

    bool ok = userDb.open();//打开数据库连接
    if(!ok)
    {
        //打开数据库失败
        qDebug() << "Open userDb error:" << userDb.lastError().text();
        return;
    }
    qDebug() << "open user.db success!";

    QSqlQuery query(userDb);//需要执行SQL的语句,需要先实例化一个SQL的语句对象,建议在用到该对象的时候再去实例化

    QString createTable = QString{"create table if not exists manage(username varchar(20) primary key,passwd varchar(15));"};//准备一条SQL的源语句,创建一个用户表

    if(!query.exec(createTable))//执行SQL语句对象
    {
        //创建用户表失败
        qDebug() << "create manage table error:" << query.lastError().text();
        return;
    }
    qDebug() << "create manage table success!";
}

//注册按键
void Dialog::on_regist_button_clicked()
{
    //跳转到注册页面
    ui->stackedWidget->setCurrentIndex(1);
    //修改页面背景
    QPalette pal =  ui->stackedWidget->palette();
    pal.setBrush(QPalette::Background, QBrush(QImage("../picture/7.bmp")));
    ui->stackedWidget->setAutoFillBackground(true);
    ui->stackedWidget->setPalette(pal);
}

//注销按键
void Dialog::on_delete_button_2_clicked()
{
    //跳转到注销页面
    ui->stackedWidget->setCurrentIndex(2);
    //修改页面背景
    QPalette pal =  ui->stackedWidget->palette();
    pal.setBrush(QPalette::Background, QBrush(QImage("../picture/8.bmp")));
    ui->stackedWidget->setAutoFillBackground(true);
    ui->stackedWidget->setPalette(pal);
}

//注册返回
void Dialog::on_btn_regist_back_2_clicked()
{
    //跳转到登陆页面
    ui->stackedWidget->setCurrentIndex(0);
    //修改页面背景
    QPalette pal =  ui->stackedWidget->palette();
    pal.setBrush(QPalette::Background, QBrush(QImage("../picture/main.bmp")));
    ui->stackedWidget->setAutoFillBackground(true);
    ui->stackedWidget->setPalette(pal);

    ui->lineEdit_regist_password_3->clear();
    ui->lineEdit_regist_password_4->clear();
    ui->lineEdit_regist_username_2->clear();

}

//注销返回
void Dialog::on_btn_regist_back_3_clicked()
{
    //跳转到登陆页面
    ui->stackedWidget->setCurrentIndex(0);
    //修改页面背景
    QPalette pal =  ui->stackedWidget->palette();
    pal.setBrush(QPalette::Background, QBrush(QImage("../picture/main.bmp")));
    ui->stackedWidget->setAutoFillBackground(true);
    ui->stackedWidget->setPalette(pal);

    ui->lineEdit_username_2->clear();
    ui->lineEdit_password_2->clear();
}

//注册确认按键
void Dialog::on_btn_regist_ok_2_clicked()
{
    QString name=ui->lineEdit_regist_username_2->text();
    QString password1=ui->lineEdit_regist_password_3->text();
    QString password2=ui->lineEdit_regist_password_4->text();
    //判断内容是否为空
    if(name.isEmpty() || password1.isEmpty() || password2.isEmpty())
    {
        QMessageBox::warning(this,"warn","内容未填写完");
        return;
    }
    if(password1.compare(password2,Qt::CaseSensitive)!=0)//比较密码,不忽略大小写
    {
        QMessageBox::warning(this,"warn","两次输入密码不一样");
        return;
    }

    //写入数据库
    QSqlQuery query(userDb);
    //准备一条SQL的源语句,插入一条记录
    QString insertData = QString{"insert into manage(username,passwd) values('%1','%2');"}.arg(name).arg(password1);
    //执行SQL语句对象
    if(!query.exec(insertData))
    {
        //添加用户数据失败
        qDebug() << "insert data error:" << query.lastError().text();
        return;
    }

    //清空内容
    ui->lineEdit_regist_username_2->clear();
    ui->lineEdit_regist_password_3->clear();
    ui->lineEdit_regist_password_4->clear();
    qDebug() << "insert data success!";
    QMessageBox::information(this,"info","注册成功");
}

//注销确认
void Dialog::on_btn_delect_ok_clicked()
{
    //获取用户名和密码
    QString name = ui->lineEdit_delect_username->text();
    QString passwd = ui->lineEdit_delect_password->text();
    if(name.isEmpty() || passwd.isEmpty())
    {
        QMessageBox::warning(this,"warn","用户名或密码为空!");
        return;
    }

    //查询用户数据
    QSqlQuery query(userDb);
    //准备一条SQL的源语句,修改密码数据
    QString selectUser = QString{"select username,passwd from manage where username='%1' and passwd='%2';"}.arg(name).arg(passwd);
    //执行SQL语句对象
    if(!query.exec(selectUser))
    {
        //查询失败
        qDebug() << "select user error:" << query.lastError().text();
        return;
    }
    //查找用户成功
    if(query.next())
    {
        //查到了用户的信息
        qDebug() << "The user was found through the username and password!";
        //打印查询到的结果,因为查询语句中指定了只查看username,passwd这两列,所以只有这两列的结果
        qDebug() << query.value(0).toString();  //用户名
        qDebug() << query.value(1).toString();  //密码
        selectUser = QString{"delete from manage where username='%1' and passwd='%2';"}.arg(name).arg(passwd);
        if(!query.exec(selectUser))
        {
            //删除失败
            qDebug() << "delete user error:" << query.lastError().text();
            return;
        }
        QMessageBox::information(this,"info","已经删除用户");
    }
    else
    {
        QMessageBox::warning(this,"warn","未找到该用户");
    }

    ui->lineEdit_delect_username->clear();
    ui->lineEdit_delect_password->clear();
}

//登陆
void Dialog::on_login_button_clicked()
{
    bool ok=false;
    //获取用户名和密码
    QString name = ui->lineEdit_username_2->text();
    QString passwd = ui->lineEdit_password_2->text();
    if(name.isEmpty() || passwd.isEmpty())
    {
        QMessageBox::warning(this,"warn","用户名或密码为空!");
        return;
    }

    //查询用户数据
    QSqlQuery query(userDb);
    //准备一条SQL的源语句,修改密码数据
    QString selectUser = QString{"select username,passwd from manage where username='%1' and passwd='%2';"}.arg(name).arg(passwd);
    //执行SQL语句对象
    if(!query.exec(selectUser))
    {
        //查询失败
        qDebug() << "select user error:" << query.lastError().text();
        return;
    }
    //查找用户成功
    if(query.next())
    {
        //查到了用户的信息
        //打印查询到的结果,因为查询语句中指定了只查看username,passwd这两列,所以只有这两列的结果
        ok=true;
    }
    else
    {
        QMessageBox::warning(this,"warn","未找到该用户");
    }

    if(ok){
        QMessageBox::information(this,"info","登陆成功");
        query.clear();
        userDb.close();
        userDb.removeDatabase("../user.db");
        this->hide();
        emit login_success(name,passwd);
    }

    ui->lineEdit_username_2->clear();
    ui->lineEdit_password_2->clear();
}
2、TCP初始化
bool Widget::tcp_server_init()
{
    //实例化一个TCP服务器对象
    tcpServer = new QTcpServer(this);

    //服务器绑定IP地址和端口以及监听客户端的连接
    bool ok = tcpServer->listen(QHostAddress("192.168.1.106"),6666);
    if(!ok)
    {
        qDebug() << "tcpserver listen error:" << tcpServer->errorString();
        return false;
    }

    //当监听到客户端连接请求信号,就去处理该连接
    connect(tcpServer,&QTcpServer::newConnection,this,[=](){
        //从连接列表中获取下一个连接对象
        QTcpSocket *nextTcpSocket = tcpServer->nextPendingConnection();

        //实例化一个线程对象用来处理与客户端的通信
        Server_thread *tcp_thread= new Server_thread(nextTcpSocket,&map,this);
        thread_map.insert(nextTcpSocket,tcp_thread);

        //获取对端(客户端)的IP地址和端口号
        QString clientIp = nextTcpSocket->peerAddress().toString(); //获取IP地址
        quint16 port = nextTcpSocket->peerPort();   //获取端口号
        qDebug() << "client ip:" << clientIp << " client port:" << port;

        //当客户端断开连接的时候,也要进行相应的处理
        connect(nextTcpSocket,&QTcpSocket::disconnected,this,[=](){
            //客户端断开连接
            qDebug() << "client disconnected!";
            auto it = map.find(nextTcpSocket);

            //qDebug()<<"1 val:"<<it.value();
            if (it != map.end())
            {
                if(it.value().left(8)!="android:")
                {
                    all_android("message:"+it.value());
                    list_view_delete(it.value(),front_model,front_list);
                    //该用户是stm32
                    qDebug() << "stm32 close!";
                    ui->label_stm32->setText("stm32 not conncet");
                    QMessageBox::warning(this,"stm32","stm32 close conncet");
                    if(new_client_Socket==nextTcpSocket)
                        new_client_Socket=NULL;
                }
                else
                    list_view_delete(it.value(),client_model,client_list);
                map.erase(it);
            }

            //关闭套接字
            nextTcpSocket->close();
            //延时释放套接字资源,防止其他地方有用到资源,还没用完,等他们用完了再释放
            nextTcpSocket->deleteLater();

            auto it2 =thread_map.find(nextTcpSocket);
            if (it != map.end())
            {
                Server_thread *temp=it2.value();
                temp->quit();
                temp->wait();
                qDebug()<<"thread exit";
                thread_map.erase(it2);
                qDebug()<<thread_map.size();
            }

        });

        //当套接字的连接状态发生变化的时候,会发送信号stateChanged
        connect(nextTcpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,\
                SLOT(m_on_state_changed(QAbstractSocket::SocketState)));

        tcp_thread->start();
        //tcp线程消息
        connect(tcp_thread,&Server_thread::Message,this,[=](QString text,int type,QTcpSocket *socket){
            QString time=QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");//获取当前时间
            QString str=QString{"%1 : %2"}.arg(text).arg(time);
            switch (type) {
            case Clinet:
                if (text.left(8)!="android:")
                {
                    if(select_id==NULL)
                        select_id=text;
                    if(new_client_Socket==NULL)
                        new_client_Socket=nextTcpSocket;
                    stm32_conncet=true;
                    ui->label_stm32->setText("stm32 already conncet");
                    QMessageBox::warning(this,"stm32","stm32 already conncet");
                    list_view_insert(str,front_model,front_list);
                }
                else
                    list_view_insert(str,client_model,client_list);
                break;
            case Request:
                process_request(text,socket);
                break;
            case Date:
                process_data(text);
                break;
           case Rfid:
                process_rfid_info(text);
                break;
            case Error:

                break;
            case Warning:

                break;
            }
        });
    });

    return true;
}

void Widget::m_on_state_changed(QAbstractSocket::SocketState state)
{
    //    QAbstractSocket::UnconnectedState   表示未连接状态,即还没有建立连接
    //    QAbstractSocket::HostLookupState    正在进行主机名解析的状态,即正在将主机名转换成IP地址
    //    QAbstractSocket::ConnectingState    正在尝试连接的状态
    //    QAbstractSocket::ConnectedState     已经连接的状态
    //    QAbstractSocket::BoundState         已绑定到一个本地地址的状态,等待连接请求
    //    QAbstractSocket::ClosingState       正在关闭连接的状态
    //    QAbstractSocket::ListeningState     正在监听连接请求的状态
    switch (state)
    {
    case QAbstractSocket::UnconnectedState:
        qDebug() << "no connect";
        break;
    case QAbstractSocket::HostLookupState:
        qDebug() << "The status of host name resolution in progress";
        break;
    case QAbstractSocket::ConnectingState:
        qDebug() << "The status of the attempted connection";
        break;
    case QAbstractSocket::ConnectedState:
        qDebug() << "The status of the connection";
        break;
    case QAbstractSocket::BoundState:
        qDebug() << "A state that has been bound to a local address, waiting for a connection request";
        break;
    case QAbstractSocket::ClosingState:
        qDebug() << "The status of closing the connection";
        break;
    case QAbstractSocket::ListeningState:
        qDebug() << "Listening for the status of connection requests";
        break;
    default:
        qDebug() << "Unknown status";
        break;
    }
}
3、数据库的打开或创建
//打开或创建数据库
void Widget::open_sql(QString sql_path)
{
    userDb=QSqlDatabase::addDatabase("QSQLITE");//初始化连接对象,使用静态成员函数addDatabase,参数是数据库的类型
    userDb.setDatabaseName(sql_path);//设置数据库库名,建议带路径,如果数据库存在,则直接打开,如果数据不存在,则创建再打开

    bool ok = userDb.open();//打开数据库连接
    if(!ok)
    {
        //打开数据库失败
        qDebug() << "Open userDb error:" << userDb.lastError().text();
        return;
    }
    qDebug() << "open user.db success!";

    QSqlQuery query(userDb);//需要执行SQL的语句,需要先实例化一个SQL的语句对象,建议在用到该对象的时候再去实例化

    QString createTable = QString{"create table if not exists date(id int,sta int,current vachar(20),voltage_series varchar(15),elevation varchar(15),\
    light varchar(15),temperature varchar(15),humidity varchar(15),pressure varchar(15),tds2 varchar(15),erro int,warn int);"};//准备一条SQL的源语句,创建一个用户表

    if(!query.exec(createTable))//执行SQL语句对象
    {
        //创建date表失败
        qDebug() << "create date table error:" << query.lastError().text();
        return;
    }
    qDebug() << "create date table success!";

    createTable= QString{"create table if not exists user(card_id varchar(20) primary key,name varchar(15),age varchar(15),balance varchar(10));"};//准备一条SQL的源语句,创建一个用户表

    if(!query.exec(createTable))//执行SQL语句对象
    {
        //创建user表失败
        qDebug() << "create user table error:" << query.lastError().text();
        return;
    }
    qDebug() << "create user table success!";

    createTable = QString{"create table if not exists device(id int primary key,place varchar(20));"};//准备一条SQL的源语句,创建一个用户表

    if(!query.exec(createTable))//执行SQL语句对象
    {
        //创建user表失败
        qDebug() << "create device table error:" << query.lastError().text();
        return;
    }
    qDebug() << "create device table success!";

    createTable = QString{"create table if not exists drink_info(id int NOT NULL,device_id int NOT NULL,drink_count int NOT NULL,hours int NOT NULL,minutes int NOT NULL,seconds int NOT NULL);"};//准备一条SQL的源语句,创建一个用户表

    if(!query.exec(createTable))//执行SQL语句对象
    {
        //创建user表失败
        qDebug() << "create drink_info table error:" << query.lastError().text();
        return;
    }
    qDebug() << "create drink_info table success!";
}
4、最终效果如图

四、前端应用终端(QT安卓开发)

1、主要代码如下

需要在qt的.pro文件中添加

QT += network

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    socket_client=new QTcpSocket();//套接字初始化

    //接收到消息触发readyRead()
    connect(socket_client,&QTcpSocket::readyRead,this,[=](){
        //读取服务器发来的数据
        QString text = socket_client->readAll();
        if(text.left(5) == "date:")//主要用于接收饮水机信息,添加未保存的饮水机设备
        {
            deal_date(text.mid(5));
        }
        else if(text.left(8)=="message:")//主要用于清理断网饮水机设备
        {
            deal_message(text.mid(8));
        }
        else if(text.left(12) == "drink_count:")//用于接收查询到饮水量结果
        {
            qDebug()<<text;
            if(text.left(13).mid(12).toInt()==1)
            {
                ui->drink_count_lineEdit->setText(text.mid(14));
            }
            else if(text.left(13).mid(12).toInt()==2)
            {

                ui->user_drink_count_lineEdit->setText(text.mid(14));
            }
        }
        qDebug()<<"recv:"+text;
    });

    //连接成功触发connected
    connect(socket_client,&QTcpSocket::connected,this,[=](){
        QString s;
        QString str = QString{"user:android:%1"}.arg(name);
        socket_client->write(str.toUtf8());
        s=QString("wifi连接ip:%1,port:%2").arg(ui->ip_lineEdit->text()).arg(ui->port_lineEdit->text());
        QMessageBox::information(this,"提示","连接成功");
        ui->message_label->setText(s);
        ui->ip_lineEdit->setText("");
        ui->port_lineEdit->setText("");
        connec_success=true;
    });

    //连接断开触发disconnected
    connect(socket_client,&QTcpSocket::disconnected,this,[=](){
        QMessageBox::information(this,"提示","连接断开");
        connec_success=false;
    });


    //liset_view
    user_model=new QStringListModel(this);
    ui->listView->setModel(user_model);
    connect(ui->listView,&QListView::clicked,this,[=](QModelIndex index){
        QString strTemp;
        strTemp = index.data().toString();
        if(strTemp!=select_id)
        {
            //判断容器中是否存在选中的设备
            if(device_vector.indexOf(strTemp)!=-1)
            {
                select_id=strTemp;
                ui->id_lineEdit->setText("");
                ui->place_lineEdit->setText("");
                ui->comboBox->setCurrentIndex(Reposition);
                ui->tds_lineEdit->setText("");
            }
        }
    });

    //下拉框
    ui->comboBox->addItem("复位");
    ui->comboBox->addItem("暂停");
    ui->comboBox->addItem("正常");
    ui->comboBox->setCurrentIndex(Reposition);

    ui->stackedWidget->setCurrentIndex(0);
}

Widget::~Widget()
{
    delete ui;
}

//连接TCP
void Widget::on_connect_button_clicked()
{
    QString s;
    if(name==NULL)
    {
        name=ui->android_name_lineEdit->text();
        if(name=="")
        {
            QMessageBox::warning(this,"warn","名称不能为空");
            return;
        }
    }
    if(ui->ip_lineEdit->text().isEmpty() || ui->port_lineEdit->text().isEmpty())//ip和port为空
    {
        QMessageBox::warning(this,"警告","ip或port不能为空");
        return;
    }

    socket_client->abort(); //取消已有的连接
    socket_client->connectToHost(ui->ip_lineEdit->text(),ui->port_lineEdit->text().toInt());//连接到主机,这里从界面获取主机地址和端口号
}

//断开连接
void Widget::on_close_button_clicked()
{
    socket_client->abort();
    ui->message_label->setText("wifi未连接");
}

//自动填写ip——port
void Widget::on_auto_btn_clicked()
{
    ui->ip_lineEdit->setText("192.168.1.106");
    ui->port_lineEdit->setText("6666");
}

//处理date
void Widget::deal_date(QString text)
{
    int i=0;
    QString date[20];
    QStringList list = text.split(",");
    QStringList::iterator it;
    for ( it= list.begin(); it != list.end(); ++it) {
        QString s=*it;
        date[i]=*it;
        i++;
    }
    if(i==6 && connec_success)
    {
        if(device_vector.indexOf(date[0])==-1)//容器中未找到饮水设备
        {
            if(device_vector.isEmpty())
                select_id=date[0];
            else
                device_vector.push_back(date[0]);
            list_view_insert(date[0],user_model,user_list);
        }
        if(date[0]==select_id)
        {
            ui->id_lineEdit->setText(date[0]);
            ui->place_lineEdit->setText(date[1]);
            ui->comboBox->setCurrentIndex(date[2].toInt());
            ui->tds_lineEdit->setText(date[3]);
            if(date[4].toInt()==0)
                ui->erro_lineEdit->setText("无");
            else
                ui->erro_lineEdit->setText("Error1");
            if(date[5].toInt()==0)
                ui->warn_lineEdit->setText("无");
            else
                ui->warn_lineEdit->setText("Warning1");
        }
    }
}
//处理message,主要用于清理断网饮水机设备
void Widget::deal_message(QString text)
{
    int ret=device_vector.indexOf(text);
    if(ret!=-1)//容器中找到饮水机设备
        device_vector.remove(ret);
    list_view_delete(text,user_model,user_list);
    if(text==select_id)
    {
        ui->id_lineEdit->setText("");
        ui->place_lineEdit->setText("");
        ui->comboBox->setCurrentIndex(Reposition);
        ui->tds_lineEdit->setText("");
    }
}
//添加list——view成员
void Widget::list_view_insert(QString str,QStringListModel *model,QStringList list)
{
    list.append(str);
    model->setStringList(list);
}
//删除list——view成员
void Widget::list_view_delete(QString str,QStringListModel *model,QStringList list){
    QStringList::iterator it;
    for(it = list.begin(); it != list.end(); ++it){
        QString temp=*it;
        if(temp==str)
        {
            list.removeOne(*it);
            break;
        }
    }
    model->setStringList(list);
}

void Widget::on_comboBox_currentIndexChanged(int index)
{
    QString s=NULL;
    qDebug()<<index;
    s=QString{"request:sta:%1:%1"}.arg(index).arg(select_id);
    socket_client->write(s.toUtf8());
}
//设置饮水机目标温度
void Widget::on_set_tar_btn_clicked()
{
    QString str=ui->set_lineEdit->text();
    if(connec_success==false)
        QMessageBox::warning(this,"warn","wifi 未连接");
    if(str!="")
    {
        QString s=QString{"request:set_tar_temp:%1:%2"}.arg(str).arg(select_id);
        socket_client->write(s.toUtf8());
    }
    else
        QMessageBox::warning(this,"warn","内容不能为空");
}
//饮水机饮水情况查询
void Widget::on_device_check_btn_clicked()
{
    QString ID=ui->device_id_lineEdit->text();
    QString hourse1=ui->house_lineEdit->text();
    QString hourse2=ui->house2_lineEdit->text();
    QString min1=ui->min_lineEdit->text();
    QString min2=ui->min2_lineEdit->text();
    if(connec_success==false)
        QMessageBox::warning(this,"warn","wifi 未连接");
    qDebug()<<ID<<" "<<hourse1<<" "<<hourse2<<" "<<min1<<" "<<min2<<" ";
    if(ID!="" && hourse1!="" && hourse2!="" && min1!="" && min2!="")
    {
        int time1=hourse1.toInt()*60+min1.toInt();
        int time2=hourse2.toInt()*60+min2.toInt();
        QString s=QString{"request:sql:1:select drink_count from drink_info where device_id==%1 and (hours*60+minutes)>=%2 and (hours*60+minutes)<=%3;"}.arg(ID).arg(time1).arg(time2);
        qDebug()<<s;
        socket_client->write(s.toUtf8());
    }
    else
        QMessageBox::warning(this,"warn","内容不能为空");
}
//用户饮水情况查询
void Widget::on_user_check_btn_clicked()
{
    QString ID=ui->user_id_lineEdit->text();
    QString hourse1=ui->user_house_lineEdit->text();
    QString hourse2=ui->user_houser2_lineEdit->text();
    QString min1=ui->user_min_lineEdit->text();
    QString min2=ui->user_min2_lineEdit->text();
    if(connec_success==false)
        QMessageBox::warning(this,"warn","wifi 未连接");
    if(ID!="" && hourse1!="" && hourse2!="" && min1!="" && min2!="")
    {
        int time1=hourse1.toInt()*60+min1.toInt();
        int time2=hourse2.toInt()*60+min2.toInt();
        QString s=QString{"request:sql:2:select drink_count from drink_info where id==%1 and (hours*60+minutes)>=%2 and (hours*60+minutes)<=%3;"}.arg(ID).arg(time1).arg(time2);
        qDebug()<<s;
        socket_client->write(s.toUtf8());
    }
    else
        QMessageBox::warning(this,"warn","内容不能为空");
}
//前往查询界面
void Widget::on_check_btn_clicked()
{
    ui->stackedWidget->setCurrentIndex(1);
}
//返回主界面
void Widget::on_check_return_btn_clicked()
{
    ui->stackedWidget->setCurrentIndex(0);
    ui->house_lineEdit->setText("");
    ui->house2_lineEdit->setText("");
    ui->min_lineEdit->setText("");
    ui->min2_lineEdit->setText("");
    ui->user_house_lineEdit->setText("");
    ui->user_houser2_lineEdit->setText("");
    ui->user_min_lineEdit->setText("");
    ui->user_min2_lineEdit->setText("");
    ui->device_id_lineEdit->setText("");
    ui->drink_count_lineEdit->setText("");
    ui->user_drink_count_lineEdit->setText("");
    ui->user_id_lineEdit->setText("");
}
2、最终效果如图

五、巡检装置(QT安卓开发)

1、主要代码如下

需要在qt的.pro文件中添加

QT +=bluetooth androidextras core-private
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    localDevice= new QBluetoothLocalDevice(this);//实例化对象,用于对本地设备进行操作
    socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);//实例化对象,用于连接
    discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);//实例化对象,用于搜索附近设备


    //发现设备时会触发deviceDiscovered信号
    connect(discoveryAgent,&QBluetoothDeviceDiscoveryAgent::deviceDiscovered,this,&Widget::addBlueToothDevicesToList);
    //connect(discoveryAgent,SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),this,SLOT(addBlueToothDevicesToList(QBluetoothDeviceInfo)));
    //搜寻完设备时会触发finished信号
    connect(discoveryAgent,&QBluetoothDeviceDiscoveryAgent::finished,this,[=](){//刷新完成
        ui->search_button->setEnabled(true);
        QMessageBox::information(this,"提示","刷新完成");
    });

    //双击listwidget的项目,连接蓝牙
    connect(ui->device_list,&QListWidget::itemActivated,this,&Widget::connectBLE);

    //蓝牙连接设备成功后会停止搜索设备,显示连接成功
    connect(socket,&QBluetoothSocket::connected,this,[=](){
        discoveryAgent->stop();  //停止搜索设备
        QMessageBox::information(this, tr("提示"), tr("连接成功!"));
        connect_sta=true;//已经连接
        ui->connect_label->setText("蓝牙已连接");
        timer->setInterval(5000);
        timer->start();
        connect(timer,&QTimer::timeout,this,[=](){
            timer_over++;
            QString over=QString{"%1"}.arg(timer_over);
            QString s=NULL;
            ui->count_lineEdit->setText(over);
            s="connection successful ack#";
            socket->write(s.toUtf8());
        });
    });

    //蓝牙连断开连接后,会显示已断开连接
    connect(socket,&QBluetoothSocket::disconnected,this,[=](){
        QMessageBox::information(this, tr("提示"), tr("已断开连接"));
        timer->stop();
        timer_over=0;
        ui->count_lineEdit->setText("0");
    });

    //接受到上位机传来的数据后显示,会触发接受数据函数
    connect(socket,&QBluetoothSocket::readyRead,this,[=](){
        QString strData = socket->readAll();//读取所有数据
        recv_buf.append(strData);//未接收到停止标志
        if(recv_buf.contains('#'))//如果接收到停止标志
        {
            recv_buf.remove('#');
            ui->recv_textEdit->setText(recv_buf);
            if(recv_buf.left(8)=="message:")
            {
                QStringList list = recv_buf.mid(8).split(",");
                QString date[20];
                int i=0;
                QStringList::iterator it;
                for(it=list.begin();it!=list.end();it++)
                    date[i++]=*it;
                if(i==7)
                {
                    ui->id_lineEdit->setText(date[0]);
                    ui->v1_lineEdit->setText(date[1]);
                    ui->v2_lineEdit->setText(date[2]);
                    ui->error_lineEdit->setText(date[3]);
                    ui->warn_lineEdit->setText(date[4]);
                    ui->tar_temp_lineEdit->setText(date[5]);
                    ui->sta_comboBox->setCurrentIndex(date[6].toInt());
                }
            }
            recv_buf.clear();
        }
    });

    //断开连接触发disconnected
    connect(socket,&QBluetoothSocket::disconnected,this,[=](){
        connect_sta=false;//断开连接
    });
    timer = new QTimer(this);
    ui->count_lineEdit->setText("0");
}

Widget::~Widget()
{
    delete ui;
}

//权限检测
bool Widget::checkPermission()
{
   qDebug() << "to check";
    QtAndroid::PermissionResult r = QtAndroid::checkPermission(ACCESS_COARSE_LOCATION);
     if(r == QtAndroid::PermissionResult::Denied) {
         qDebug() << "checking";
         QtAndroid::requestPermissionsSync( QStringList() <<ACCESS_COARSE_LOCATION ,10);
         r = QtAndroid::checkPermission(ACCESS_COARSE_LOCATION);
         if(r == QtAndroid::PermissionResult::Denied) {
             qDebug() << "check flase";
              return false;
         }
         qDebug() << "check true";
    }
    return true;
}

//打开蓝牙
void Widget::on_open_button_clicked()
{
    if(localDevice->hostMode()==QBluetoothLocalDevice::HostPoweredOff)//蓝牙未打开
    {
        localDevice->powerOn();//打开蓝牙
        //discoveryAgent->start();//开始扫描附近蓝牙
    }
    else
        QMessageBox::information(this,"提示","蓝牙已经打开");
    ui->open_label->setText("蓝牙打开");
}

//关闭蓝牙
void Widget::on_close_Button_clicked()
{
    if(localDevice->hostMode()==QBluetoothLocalDevice::HostPoweredOff)//蓝牙已经关闭
    {
        QMessageBox::information(this,"提示","蓝牙已经关闭");
    }
    else
        localDevice->setHostMode(QBluetoothLocalDevice::HostPoweredOff);//关闭蓝牙
    ui->open_label->setText("蓝牙关闭");
}

//关闭连接
void Widget::on_close_button_clicked()
{
    socket->disconnectFromService();//断开连接
    QMessageBox::information(this,"提示","断开连接");
    ui->connect_label->setText("蓝牙未连接");
}

//搜寻附近蓝牙
void Widget::on_search_button_clicked()
{
    checkPermission();
    discoveryAgent->start();//开始扫描
    ui->device_list->clear();//清除
}

//添加蓝牙列表
void Widget::addBlueToothDevicesToList(const QBluetoothDeviceInfo &info)
{
    QString str=QString("%1 %2").arg(info.address().toString()).arg(info.name());//搜寻到的蓝牙地址和姓名
//    ui->recv_area_edit->setTextColor(Qt::blue);//设置颜色
//    ui->recv_area_edit->append(str);//追加到收发区
    bool isEmpty= ui->device_list->findItems(str,Qt::MatchExactly).isEmpty();//搜寻列表是否为没有这个
    if(isEmpty)
    {
        QListWidgetItem *item = new QListWidgetItem(str);
        QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(info.address());
        /* 蓝牙状态pairingStatus,Pairing枚举类型
         * 0:Unpaired没配对
         * 1:Paired配对但没授权
         * 2:AuthorizedPaired配对且授权 */
        if (pairingStatus == QBluetoothLocalDevice::Paired || pairingStatus == QBluetoothLocalDevice::AuthorizedPaired )//已配对
            item->setTextColor(QColor(Qt::red));
        else//未配对
            item->setTextColor(QColor(Qt::black));
        ui->device_list->addItem(item);//加入
    }

}

//蓝牙连接
void Widget::connectBLE(QListWidgetItem *item)
{
    QString str=item->text();
    int index = str.indexOf(' ');
    if (index == -1)
        return;
    QBluetoothAddress address(str.left(index));//地址
    QString name(str.mid(index + 1));//姓名
    QMessageBox::information(this,tr("提示"),tr("设备正在连接中..."));
    socket->connectToService(address, QBluetoothUuid(serviceUuid) ,QIODevice::ReadWrite);//address:地址,QBluetoothUuid(serviceUuid):标识符,QIODevice::ReadWrite:读写
}

//设置温度
void Widget::on_send_button_clicked()
{
    timer->destroyed();
    if(ui->set_tar_temp_lineEdit->text().isEmpty())
    {
        QMessageBox::information(this,tr("警告"),tr("设置目标温度为空,发送失败"));
        return ;
    }
    QString s=QString{"set_tar_temp:%1#"}.arg(ui->set_tar_temp_lineEdit->text());
    socket->write(s.toUtf8());
    timer->setInterval(5000);
    timer->start();

}

//蓝牙状态
bool Widget::blueStates()
{
    if(!connect_sta)
    {
        QMessageBox::information(this, "错误", "蓝牙未连接");
        return false;
    }
    return true;
}

//设置状态
void Widget::on_set_sta_bnt_clicked()
{
    timer->destroyed();
    QString s=QString{"set_sta:%1#"}.arg(ui->sta_comboBox->currentIndex());
    socket->write(s.toUtf8());
    timer->setInterval(5000);
    timer->start();
}
2、效果如图

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,需要在STM32F407VET6上搭载FreeRTOS操作系统,并且实现串口通信功能。接下来,我们可以通过消息队列来实现控制板载LED的开关。 在主函数中,我们创建一个消息队列,然后创建一个任务来接收串口命令并将命令发送到消息队列中。另外,我们还需要创建一个任务来读取消息队列中的数据,并根据命令来控制LED的开关。 下面是一个简单的示例代码: ```c #include "FreeRTOS.h" #include "task.h" #include "queue.h" #include "stm32f4xx.h" #include "stm32f4xx_hal.h" #define LED_PIN GPIO_PIN_13 #define LED_PORT GPIOC TaskHandle_t xTask1Handle; TaskHandle_t xTask2Handle; QueueHandle_t xQueue; void LED_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitStruct.Pin = LED_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct); } void USART1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; USART_InitTypeDef USART_InitStruct = {0}; NVIC_InitTypeDef NVIC_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_USART1_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); USART_InitStruct.BaudRate = 115200; USART_InitStruct.WordLength = USART_WORDLENGTH_8B; USART_InitStruct.StopBits = USART_STOPBITS_1; USART_InitStruct.Parity = USART_PARITY_NONE; USART_InitStruct.Mode = USART_MODE_TX_RX; USART_InitStruct.CLKPolarity = USART_POLARITY_LOW; USART_InitStruct.CLKPhase = USART_PHASE_1EDGE; USART_InitStruct.CLKLastBit = USART_LASTBIT_DISABLE; HAL_USART_Init(&USART_InitStruct); NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; HAL_NVIC_Init(&NVIC_InitStruct); HAL_NVIC_EnableIRQ(USART1_IRQn); } void USART1_IRQHandler(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; char cReceivedChar = 0; if (__HAL_USART_GET_FLAG(&huart1, USART_FLAG_RXNE) != RESET) { __HAL_USART_CLEAR_FLAG(&huart1, USART_FLAG_RXNE); cReceivedChar = (char)(huart1.Instance->DR & 0xFF); xQueueSendFromISR(xQueue, &cReceivedChar, &xHigherPriorityTaskWoken); } portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } void vTask1(void *pvParameters) { char cCommand = 0; while (1) { if (HAL_USART_Receive(&huart1, (uint8_t *)&cCommand, 1, 1000) == HAL_OK) { xQueueSend(xQueue, &cCommand, 0); } } } void vTask2(void *pvParameters) { char cReceivedChar = 0; while (1) { if (xQueueReceive(xQueue, &cReceivedChar, portMAX_DELAY) == pdTRUE) { switch (cReceivedChar) { case 'o': HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET); break; case 'c': HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET); break; default: break; } } } } int main(void) { HAL_Init(); LED_Init(); USART1_Init(); xQueue = xQueueCreate(10, sizeof(char)); xTaskCreate(vTask1, "Task 1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, &xTask1Handle); xTaskCreate(vTask2, "Task 2", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, &xTask2Handle); vTaskStartScheduler(); while (1) { } } ``` 在该示例中,我们使用消息队列来实现串口命令的传递和控制LED的开关。当接收到字符'o'时,我们将板载LED打开,当接收到字符'c'时,我们将板载LED关闭。通过这种方式,我们可以通过串口来控制板载LED的开关。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值