Files
FreeRTOS_LoRa_Environment_A…/README.md

105 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FreeRTOS_LoRa_Environment_Assistant
## 项目概述:
**该项目为裸机开发“LoRa 2Nodes Demo”的升级版。**
不仅移植了FreeRTOS操作系统而且完善了传输协议。减少了远程操控延迟的同时保证项目的稳定运行。基于 STM32F103C8T6 最小系统板与正点原子 ATK-LORA 模块开发智能环境监测系统,构建双 LoRa 节点网络,通过 I2C、SPI、单总线等常用协议采集温湿度等传感器数据采用定点组网模式与 LoRa 网关通信,结合 Wi-Fi 模块将数据推送至基于 MQTT 协议的 EMQX Broker 服务器;采用 FreeRTOS 进行任务调度优化,保障多线程数据采集与传输;在每一个节点通过 LVGL 图形库添加可视化界面,通过 OLED 显示屏实时显示数据信息和可操控设备;通过开发 Android App集成实时数据可视化、远程设备控制、安全监控及异常报警等功能。
## 项目实现:
### 一、使用FreeRTOS操作系统
**优点及作用:**
可保证每个任务不会因为延迟被阻塞,大幅度减小远程操控的延迟。
#### 以环境监测节点为例:
**1、创建任务**
创建了LED状态灯任务、温湿度检测任务、向网关节点传输数据、从网关节点接收数据和检测执行器指令控制LED灯五个任务。
**2、创建消息队列**
温湿度检测任务将采集到的温湿度数据通过队列发送到传输数据任务,实现数据的保护。
**3、创建中断触发消息队列**
创建串口触发消息队列当串口3接收到消息触发中断后将数据包传送到消息队列同时创建一个任务用于接收消息队列并用于后续的检测接收到的数据包是否完整、是否有相关指令。
**4、创建二值信号量**
创建检测LED指令消息队列调用LoRa.c中检测数据包的函数检测消息队列是否有相关的控制指令若有相关的控制指令则触发信号量在其他任务中判断该信号量是否被触发若被触发则控制LED灯开启反之关闭。
### 二、自定义传输协议:
自定义传输协议仿照了ModBus传输协议其完善性相比有欠缺但是可满足非大型项目的基本使用。在自定义传输协议中使用十六进制数字进行传输分别由网关节点和子节点这里拿子节点举例。子节点的传输协议主要分为帧头、传感器ID、传感器数据、执行器ID、执行器指令和帧尾。其中帧头也是节点ID用于声明自己是哪个节点上发的数据传感器ID用于声明自己是什么传感器传感器数据如DHT11温湿度数据执行器ID也是同理用于声明自己是什么执行器执行器指令一般是0或10为关、1为开用于在执行网关节点下发的控制指令后的返回结果告诉网关节点是开了还是关了是否成功若失败了是什么原因最后一个是帧尾为帧头倒写添加帧头和帧尾的目的是在网关接收多个节点的数据是验证其数据是否完整不会被其他上传的数据打断。
**1、节点**
*节点ID*
D1为子节点1D2为子节点2以此类推。
*传感器ID*
EA为DHT11温湿度传感器EB为MQ2烟雾浓度传感器、EC为火焰传感器、ED为光敏传感器。
*传感器数据:*
如17、3517为温度数据、35为湿度数据将其转化为10进制分别为23℃、湿度53%。
*执行器ID*
FA为LED灯、FB为小风扇、FC为加湿器、FD为蜂鸣器、FE为舵机。
*执行器数据*
0为关、1为开。
*帧尾:*
D1倒过来就是1D以此类推。
| 目标地址 | 目标信道 | 帧头也是节点ID | 传感器ID | 传感器数据 | 执行器ID | 执行器数据 | 帧尾(为帧头倒写) |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| 03 E9 | 17 | D1 | EA | 17 35 | FA | 01 | 1D |
## 本系统在FreeRTOS中引用的api
### 一、多任务创建与运行
**1、概念**
通过定义任务句柄、名称、优先级从而实现多任务同时在单核的单片机中以并发或抢占的形式运行。在FreeRTOS中时钟源频率通常被设定为1kHz(#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ))时间片1tick也就是1ms。在任务优先级相同的情况下多个任务按照一个时间片切换运行也就是轮询式调度当其中一个任务的优先级较高时数值较高该任务会打断其他任务的运行也就是抢占式调度。在创建任务后会产生任务控制块简称TCBTCB在切换任务的时候会将创建任务的指针保存到创建的栈里也就是保存任务现场直到任务切换过来后再复原现场。FreeRTOS的任务分为4个状态分别为就绪态、运行态、挂起态和阻塞态当其中一个任务遇到延时等情况会进入到阻塞态会执行其他任务知道延时结束低优先级任务被高优先级任务打断也同理。
**2、使用到的api函数**
- 任务函数:
```c
void vTask(void *<>);
```
- 创建任务函数:
```c
TaskHandle_t xTaskHandler;
xTaskCreate(
(TaskFunction_t ) vTask,
(char * ) "<定义一个任务名称>",
(configSTACK_DEPTH_TYPE) 1024,
(void * ) NULL,
(UBaseType_t ) <>
(TaskHandle_t * ) &xTaskHandler);
```
- 启动任务调度函数:
```c
vTaskStartScheduler();
```
### 二、消息队列:
**1、概念**
在初始化消息队列函数种,有两个参数,第一个是队列的长度,也就是这个队列能存放多少个数据第二个参数就是每个数据的大小单位为字节在stm32中uint8_t指针的长度一般是4字节。在多个任务需要共用一个变量值以传递数据的时候使用消息队列可以很好地保护数据的传输不会被其他用到相关变量的任务干扰。消息队列通过发送队列和接收队列的函数将不同的任务搭建起一个传递参数的桥梁从而确保传递数据时不被其他任务干扰造成数据的误差。如DHT11接收到温湿度数据将温湿度数据传输到LoRa发送数据的任务中。
**2、使用到的api函数**
- 创建队列句柄:
```c
QueueHandle_t xQueueHandler;
```
- 创建队列:
```c
xQueueHandler = xQueueCreate(
(UBaseType_t) <>,
(UBaseType_t) <>);
```
- 发送队列也就是将数据存入队列等待接收接收时长从0到portMAX_DELAY如下所示。
```c
xQueueSend(xQueueHandler, <>, pdMS_TO_TICKS(10));
```
- 在中断中发送队列(在串口初始化中设置中断抢占优先级配置拉满,且需要设置上下文转换以免造成阻塞):
```c
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(xQueueUsart3IrqHdlr, &ulRxData, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
```
- 接收队列,可接收普通和中断的队列:
```c
xQueueReceive(xQueueHandler, <>, portMAX_DELAY);
```
### 三、信号量:
**1、概念**
信号量一般用于任务管理。常用的分为二值信号量、计数型信号量和互斥信号量3种信号量,其中二值信号量全称为二进制信号量用于任务同步类似于状态机而后两者分别用于多资源和单资源的管理资源管理就相当于停车场多资源管理相当于停车场的多个车位若停车位停满车就不能停车了单资源同理。这三种信号量都只有0或1两种值。使用信号量的过程以二值信号量为例为创建二值信号量->释放二值信号量->获取二值信号量。释放二值信号量共有两个函数释放任务中信号量和释放中断中信号量。信号量和状态机类似释放信号量就相当于改变标志位而获取信号量相当于获取改变的标志位以决定是否改变执行器的状态。由于只有0和1两种值因此适用于只有开和关两种状态的执行器。
**2、使用到的api函数**
- 动态创建二值信号量:
```c
SemaphoreHandle_t xSemaphoreHandler;
```
- 发送信号量:
```c
xSemaphoreGive(xSemaphoreHandler);
```
- 接收信号量:(接收句柄、等待时长)
```c
if (xSemaphoreTake(xSemaphoreHandler, pdMS_TO_TICKS(10)) == pdTRUE);
```