项目实现逻辑：
一、环境监测节点：
1、创建任务：创建了led状态任务、温湿度检测任务和向网关节点传输数据三个任务。
2、创建消息队列：温湿度检测任务将采集到的温湿度数据通过队列发送到传输数据任务，实现数据的保护。在中断服务函数中创建串口接收队列，同时新建一个所有执行器检测状态的任务，用于分析串口队列数据是否符合触发条件，若符合则触发二值信号量，反之则不触发。
3、创建中断触发消息队列：在串口3创建消息队列，当串口3接收到消息触发中断后，将消息传送到消息队列，同时创建一个任务用于接收消息队列。
本系统在FreeRTOS中引用的api：
一、多任务创建与运行：
1、概念：通过定义任务句柄、名称、优先级，从而实现多任务同时在单核的单片机中以并发或抢占的形式运行。在FreeRTOS中，时钟源频率通常被设定为1kHz(#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ))，时间片1tick也就是1ms。在任务优先级相同的情况下，多个任务按照一个时间片切换运行，也就是轮询式调度；当其中一个任务的优先级较高时（数值较高），该任务会打断其他任务的运行，也就是抢占式调度。在创建任务后，会产生任务控制块，简称TCB，TCB在切换任务的时候，会将创建任务的指针保存到创建的栈里，也就是保存任务现场，直到任务切换过来后再复原现场。FreeRTOS的任务分为4个状态，分别为就绪态、运行态、挂起态和阻塞态，当其中一个任务遇到延时等情况，会进入到阻塞态，会执行其他任务知道延时结束，低优先级任务被高优先级任务打断也同理。
2、使用到的api函数：
任务函数：void vTask(void *<参数>);
创建任务函数：TaskHandle_t xTaskHandler;
xTaskCreate(
           (TaskFunction_t        ) vTask,
           (char *                ) "<定义一个任务名称>", 
           (configSTACK_DEPTH_TYPE) 1024,
           (void *                ) NULL, （若有参数则写参数名称）
           (UBaseType_t           ) 2,（优先级，数字越大优先级越高）
           (TaskHandle_t *        ) &xTaskHandler);
启动任务调度函数：vTaskStartScheduler();
二、消息队列：
1、概念：在初始化消息队列函数种，有两个参数，第一个是队列的长度,也就是这个队列能存放多少个数据；第二个参数就是每个数据的大小，单位为字节，在stm32中uint8_t指针的长度一般是4字节。在多个任务需要共用一个变量值以传递数据的时候，使用消息队列可以很好地保护数据的传输不会被其他用到相关变量的任务干扰。消息队列通过发送队列和接收队列的函数，将不同的任务搭建起一个传递参数的桥梁，从而确保传递数据时不被其他任务干扰，造成数据的误差。如DHT11接收到温湿度数据，将温湿度数据传输到LoRa发送数据的任务中。
2、使用到的api函数：
三、信号量：
1、概念：信号量一般用于任务管理。常用的分为二值信号量、计数型信号量和互斥信号量3种信号量,其中二值信号量全称为二进制信号量，用于任务同步，类似于状态机，而后两者分别用于多资源和单资源的管理，资源管理就相当于停车场，多资源管理相当于停车场的多个车位，若停车位停满车就不能停车了，单资源同理。这三种信号量都只有0或1两种值。使用信号量的过程，以二值信号量为例，为创建二值信号量->释放二值信号量->获取二值信号量。释放二值信号量共有两个函数，释放任务中信号量和释放中断中信号量。信号量和状态机类似，释放信号量就相当于改变标志位，而获取信号量相当于获取改变的标志位以决定是否改变执行器的状态。由于只有0和1两种值，因此适用于只有开和关两种状态的执行器。
2、使用到的api函数：
动态创建二值信号量：