目录
前言:
本系统为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();
}