基于STM32的阿里云智能农业大棚
目录
前言:
项目效果演示:
一、简介
二、硬件需求准备
三、硬件框图
四、CubeMX配置
4.1、按键、蜂鸣器GPIO口配置
4.2、ADC输入配置
4.3、IIC——驱动OLED
4.4、DHT11温湿度读取
4.5、PWM配置——光照灯、水泵、风扇
4.6、串口——esp8266模块
4.7、定时器配置——按键消抖所需
五、部分代码实现思路
5.1、菜单实现思路
5.2、OLED驱动
5.3、按键消抖
5.4、DHT11温湿度驱动
5.5、光照强度、土壤湿度ADC转换
5.6、esp8266连接阿里云
六、功能拓展
前言:
注意事项:本项目使用的是u8g2库进行显示驱动OLED,需要学会移植或者直接使用我的模板,具体可以看我的这个博文:手把手移植基于STM移植U8g2图形库教程——0.96寸OLED(附送整理资料)
买来的esp8266模可能不支持连接阿里云,需要烧写MQTT固件,具体操作可以看看我的这个博文:手把手连接阿里云教程
里面都会有所需的固件和工具和教程!
项目效果演示:
智能农业大棚
一、简介
这可以作为一个简单的课设或者用来水毕设的一个小项目,总体来说挺简单的,我做完后就打算开源出来大家一起学习一下,具体功能如下:
1、按键:
按键一:切换菜单
按键二:切换选项
按键三:+-或调节风扇 | 水泵 | 灯光
按键四:确认 | 连接阿里云平台
2、液晶屏幕显示:
菜单1:主界面显示空气温湿度,土壤湿度,光照强度,检测是否超过或低于临界值
菜单2:临界值调节
菜单3:风扇手动开关
菜单4:灯光手动开关
菜单5:水泵手动开关
菜单6:阿里云连接菜单界面
3、土壤湿度、空气温湿度、光照强度高于或低于临界值,蜂鸣器就会响
4、土壤湿度过低自动打开水泵
5、光照强度过低自动打开照明灯
6、空气温度过高会自动打开风扇
7、连接阿里云平台,可以在电脑上面查看各个数据
二、硬件需求准备
1、STM32F103最小核心开发板
2、0.96寸OLED屏幕
3、按键X4
4、TB6612FNG点击驱动模块
5、水泵
6、电机(带小风扇)
7、DHT11温湿度模块
8、esp8266模块
9、蜂鸣器
10、土壤湿度传感器
11、光照强度传感器
12、照明灯模块
三、硬件框图

四、CubeMX配置
这里只演示配置使用的外设配置过程!
4.1、按键、蜂鸣器GPIO口配置
按键配置为上拉模式、蜂鸣器低电平输出有效

4.2、ADC输入配置
ADC为读取土壤湿度和光照强度的ADC数值,这里开启两个ADC分别读取
ADC1——读取土壤湿度:

ADC2——读取光照强度:

4.3、IIC——驱动OLED

4.4、DHT11温湿度读取
这个使用过程中会对弈随意变动上下拉模式,所以这里就不配置了,使用的GPIO口为PB8
4.5、PWM配置——光照灯、水泵、风扇

4.6、串口——esp8266模块

开启中断:

4.7、定时器配置——按键消抖所需

开启中断:

最后生成工程即可。
五、部分代码实现思路
5.1、菜单实现思路
最常用的就是菜单索引法还有链表法,我这里菜单结构非常简单,所以我就使用索引法了,这里简单说一下,就是每一个菜单都对应一个结构体((这个项目没有二级界面,所以用不到进入索引和返回索引)):
typedef struct
{int current_index;//当前索引int last_index; //下一索引int enter_index; //进入索引int back_index; //返回索引void(*current)(u8g2_t u8g2);}Menu;
结构体成员包括以上的几个。
这个小项目有5个菜单页面:所以定义5个结构体(这个项目没有二级界面,所以用不到进入索引和返回索引):
Menu Menu_Table[30] =
{
// 当 下 进 返{0, 1, 0, 0,(*Home_Menu)}, //主页显示界面{1, 2, 0, 0,(*Set_Menu)}, //设置临界值界面{2, 3, 0, 0,(*Fan_Menu)}, //风扇控制界面{3, 4, 0, 0,(*Light_Menu)},//灯光控制界面{4, 5, 0, 0,(*Water_Menu)},//水泵控制界面{5, 0, 0, 0,(*Wifi_Menu)},//连接阿里云控制界面};
然后定义一个当前显示菜单的索引值和当前菜单显示绘制函数:
int Current_Menu_index = 0;//当前菜单索引
void(*Current)(u8g2_t u8g2);//当前菜单索引执行绘制函数
然后初始化好按键,按下后执行跳转:
切换下一菜单使用例子(如果是其他的可以八next换成其他的结构体成员):
Current_Menu_index = Menu_Table[Current_Menu_index].last_index;//变换索引值Current = Menu_Table[Current_Menu_index].current;//函数复制
(*Current)(u8g2);//执行绘制函数
在举一个例子帮助大家理解一下,比如,我们当前程序处在索引号为2(临界值界面),就会执行Set_Menu函数。此时,如果按下next按键,程序当前索引号就会变为3,并且执行索引号为3时候的Fan_Menu函数。以此类推!
5.2、OLED驱动
这里我是使用开源的u8g2库进行开发,这个库显示效果很好,而且也很好移植!具体可以参考我的这个博文:移植u8g2库,移植成功后使用也可以看看我的另外要给博文:u8g2库函数使用
5.3、按键消抖
我这里使用的是中断消抖:
unsigned char Key_PIN_Read()
{unsigned char Temp=0;if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == GPIO_PIN_RESET) Temp = 1;if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == GPIO_PIN_RESET) Temp = 2;if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET) Temp = 3;if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_15) == GPIO_PIN_RESET) Temp = 4;return Temp;} void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //中断回调函数
{Key_Slow++;if(Key_Slow == 10) Key_Slow = 0;//按键消抖
}void Key_Pro()
{if(Key_Slow) return;//按键减速Key_Slow=1;Key_Val=Key_PIN_Read();Key_Down=Key_Val & (Key_Val ^ Key_Old);//捕捉下降沿Key_Up = ~Key_Val & (Key_Old ^ Key_Val);//捕捉按键上降沿Key_Old=Key_Val;switch(Key_Up){case 1:break;case 2:break;case 3:break;case 4:break;}}
5.4、DHT11温湿度驱动
手动配置GPIO模式:
#define DHT_HIGHT HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET)
#define DHT_LOW HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET)
#define DHT_VALUE HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8)uint8_t datas[5];//空气温湿度数据void delay_us(uint16_t cnt)
{uint8_t i;while(cnt){for (i = 0; i < 10; i++){} cnt--;}
}void DHT_GPIO_Init(uint32_t Mode)
{GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIOB_CLK_ENABLE();GPIO_InitStruct.Pin = GPIO_PIN_8;GPIO_InitStruct.Mode = Mode;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}void DHT11_Start(void)
{DHT_GPIO_Init(GPIO_MODE_OUTPUT_PP);DHT_HIGHT;DHT_LOW;HAL_Delay(30);DHT_HIGHT;DHT_GPIO_Init(GPIO_MODE_INPUT);while(DHT_VALUE);while(!DHT_VALUE);while(DHT_VALUE);
}void Read_Data_From_DHT()
{int i;//轮int j;//每一轮读多少次char tmp;char flag;DHT11_Start();DHT_GPIO_Init(GPIO_MODE_INPUT);for(i= 0;i < 5;i++){for(j=0;j<8;j++){while(!DHT_VALUE);//等待卡g点delay_us(40);if(DHT_VALUE == 1){flag = 1;while(DHT_VALUE);}else{flag = 0;}tmp = tmp << 1;tmp |= flag;}datas[i] = tmp;}
}
数据显示以及文字显示:
void Printf_DHT11(u8g2_t u8g2,u8g2_uint_t x, u8g2_uint_t y, const uint8_t *font)
{char var_buf[100];char var_buf1[100];Read_Data_From_DHT();//读取温湿度数据u8g2_DrawXBMP(&u8g2, x, y, 16, 16, kong);u8g2_DrawXBMP(&u8g2, x+16, y, 16, 16, qi);u8g2_DrawXBMP(&u8g2, x+32, y, 16, 16, wen);u8g2_DrawXBMP(&u8g2, x+48, y, 16, 16, du);u8g2_DrawXBMP(&u8g2, x, y+16, 16, 16, kong);u8g2_DrawXBMP(&u8g2, x+16, y+16, 16, 16, qi);u8g2_DrawXBMP(&u8g2, x+32, y+16, 16, 16, wen);u8g2_DrawXBMP(&u8g2, x+48, y+16, 16, 16, du);sprintf(var_buf , ": %d.%d C",datas[2],datas[3]);sprintf(var_buf1, ": %d.%d",datas[0],datas[1]);u8g2_SetFont(&u8g2, font);u8g2_DrawStr(&u8g2, x+64, y+16, var_buf);u8g2_DrawStr(&u8g2, x+64, y+32, var_buf1);
}
5.5、光照强度、土壤湿度ADC转换
int Printf_Soil(u8g2_t u8g2,u8g2_uint_t x, u8g2_uint_t y, const uint8_t *font)
{char var[100];int value;HAL_ADC_Start(&hadc1); //启动ADC单次转换HAL_ADC_PollForConversion(&hadc1, 50); //等待ADC转换完成value = HAL_ADC_GetValue(&hadc1); //读取ADC转换数据value = 100 - value * 100 / 4096;
// Soil = value;u8g2_DrawXBMP(&u8g2, x, y, 16, 16, tu);u8g2_DrawXBMP(&u8g2, x+16, y, 16, 16, rang);u8g2_DrawXBMP(&u8g2, x+32, y, 16, 16, shi);u8g2_DrawXBMP(&u8g2, x+48, y, 16, 16, du);sprintf(var , ": %d %%", value);u8g2_SetFont(&u8g2, font);u8g2_DrawStr(&u8g2, x+64, y+16, var);return value;}int Printf_Light(u8g2_t u8g2,u8g2_uint_t x, u8g2_uint_t y, const uint8_t *font)
{char var[100];int value;HAL_ADC_Start(&hadc2); //启动ADC单次转换HAL_ADC_PollForConversion(&hadc2, 50); //等待ADC转换完成value = HAL_ADC_GetValue(&hadc2); //读取ADC转换数据value = 100 - value * 100 / 4096;
// Light = value;u8g2_DrawXBMP(&u8g2, x, y, 16, 16, guang);u8g2_DrawXBMP(&u8g2, x+16, y, 16, 16, zhao);u8g2_DrawXBMP(&u8g2, x+32, y, 16, 16, qiang);u8g2_DrawXBMP(&u8g2, x+48, y, 16, 16, du);sprintf(var , ": %d %%", value);u8g2_SetFont(&u8g2, font);u8g2_DrawStr(&u8g2, x+64, y+16, var);return value;}
5.6、esp8266连接阿里云
这里我为了保险起见就是用了很多的延迟时间(连接时间过长),你们可以自己调节时间!
#define USERNAME "test&k28qfTAtkBg" //用户名
// "test&k28qfTAtkBg"
#define PASSWORD "d7c3b27a5d8ca954487de1e1946a4a08439a2c2508242268e9d90ce178178f39" //密码
// "d7c3b27a5d8ca954487de1e1946a4a08439a2c2508242268e9d90ce178178f39"
#define CLIENTID "k28qfTAtkBg.test|securemode=2\\,signmethod=hmacsha256\\,timestamp=1736673123047|" //设备名称
// "k28qfTAtkBg.test|securemode=2\\,signmethod=hmacsha256\\,timestamp=1736673123047|"
#define PRODUCTID "k28qfTAtkBg" //产品ID
// "k28qfTAtkBg"
#define DOMAINNAME "iot-06z00fj5kcoes6j.mqtt.iothub.aliyuncs.com" //域名
// "iot-06z00fj5kcoes6j.mqtt.iothub.aliyuncs.com"
#define DEVICENAME "test"//WiFi连接函数
void Wifi_Connect()
{printf("AT\r\n");//避免报错HAL_Delay(500);printf("AT+RESTORE\r\n");//恢复出厂HAL_Delay(5000);// printf("AT+RST\r\n");//复位
// HAL_Delay(5000);printf("ATE0\r\n");//关闭回显HAL_Delay(5000);printf("AT+CWMODE=3\r\n");//设置双模式HAL_Delay(5000);printf("AT+CWJAP=\"USER_E191B0\",\"98599714\"\r\n");//设置WIFI密码和账号HAL_Delay(5000);printf("AT+MQTTUSERCFG=0,1,\"NULL\",\"%s\",\"%s\",0,0,\"\"\r\n",USERNAME,PASSWORD);//设置MQTT的username和passwordHAL_Delay(5000);printf("AT+MQTTCLIENTID=0,\"%s\"\r\n",CLIENTID); //设置CLIENTIDHAL_Delay(5000);printf("AT+MQTTCONN=0,\"%s\",1883,1\r\n",DOMAINNAME);//设置域名HAL_Delay(5000);printf("AT+MQTTSUB=0,\"/%s/%s/user/get\",1\r\n",PRODUCTID,DEVICENAME);//订阅}
六、功能拓展
后续拓展:
1、电脑阿里云平台控制单片机
2、添加语音模块
。。。。。。。。。。。。其他
后续打了PCB板子后有空会实现给大家,觉得有帮助的可以点点关注!
相关文章:
基于STM32的阿里云智能农业大棚
目录 前言: 项目效果演示: 一、简介 二、硬件需求准备 三、硬件框图 四、CubeMX配置 4.1、按键、蜂鸣器GPIO口配置 4.2、ADC输入配置 4.3、IIC——驱动OLED 4.4、DHT11温湿度读取 4.5、PWM配置——光照灯、水泵、风扇 4.6、串口——esp8266模…...
80,【4】BUUCTF WEB [SUCTF 2018]MultiSQL
53,【3】BUUCTF WEB october 2019 Twice SQLinjection-CSDN博客 上面这个链接是我第一次接触二次注入 这道题也涉及了 对二次注入不熟悉的可以看看 BUUCTF出了点问题,打不开,以下面这两篇wp作为学习对象 [SUCTF 2018]MultiSQL-CSDN博客 …...
深入探索imi框架:PHP Swoole的高性能协程应用实践
摘要 本文将介绍 imi 框架,这是一个基于 PHP Swoole 的高性能协程应用开发框架。imi 支持 HttpApi、WebSocket、TCP 和 UDP 等多种服务类型,利用 Swoole 的优化技术,使得在处理请求时响应速度远超传统的 php-fpm 方式。通过丰富的代码示例&a…...
【算法篇·更新中】C++秒入门(附练习用题目)
一.二分 1.二分查找 我们来看这样一道题: 有一个保证有序的数组a,它的长度为n。现在我们需要知道这个序列是否含有x。 数据范围:保证n<1e9 我们看到这道题之后,第一时间想到的就是暴力枚举了,可是我们发现直接枚举…...
对神经网络基础的理解
目录 一、《python神经网络编程》 二、一些粗浅的认识 1) 神经网络也是一种拟合 2)神经网络不是真的大脑 3)网络构建需要反复迭代 三、数字图像识别的实现思路 1)建立一个神经网络类 2)权重更新的具体实现 3&am…...
存储基础 -- SCSI命令格式与使用场景
SCSI命令格式与使用场景 1. SCSI命令描述符块(CDB) 1.1 CDB基本概念 SCSI命令通过**命令描述符块(CDB, Command Descriptor Block)**表示。 CDB长度:SCSI命令根据使用场景有不同长度的CDB,常见的有6字节…...
从崩溃难题看 C 标准库与 Rust:线程安全问题引发的深度思考
在软件开发的世界里,每一次技术的变革和尝试都伴随着未知的挑战。EdgeDB 团队在将部分网络 I/O 代码从 Python 迁移到 Rust 的过程中,就遭遇了一场棘手的问题,这个问题不仅暴露了 C 标准库的线程安全隐患,也让我们对 Rust 的 “安…...
【CSS入门学习】Flex布局设置div水平、垂直分布与居中
水平平均分布 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>…...
9. 神经网络(一.神经元模型)
首先,先看一个简化的生物神经元结构: 生物神经元有多种类型,内部也有复杂的结构,但是可以把单个神经元简化为3部分组成: 树突:一个神经元往往有多个树突,用于接收传入的信息。轴突:…...
R 语言 | future 包,非阻塞的执行耗时脚本
目的:有一段代码,后面暂时用不到,但是又很耗时,占了当前R session,难道只能等半个小时,等到它结束才能画图? 可以使用R多线程,在支线进程中执行耗时任务,同时不阻塞当前…...
UE学习日志#12 Niagara特效大致了解(水文,主要是花时间读了读文档和文章)
1 核心组件(官方文档阅读) 一些介绍主要来自官方文档Niagara概述 1.1 Systems 官方文档中的描述: Niagara系统是一种容器,可以放入你要构建该效果的所有内容。在这个系统中,你可以搭建不同的构建块来实现总体效果。…...
【数据结构】_链表经典算法OJ:合并两个有序数组
目录 1. 题目描述及链接 2. 解题思路 3. 程序 3.1 第一版 3.2 第二版 1. 题目描述及链接 题目链接:21. 合并两个有序链表 - 力扣(LeetCode) 题目描述: 将两个升序链表合并为一个新的 升序 链表并返回。 新链表是通过拼接给…...
Mongodb副本集群为什么选择3个节点不选择4个节点
一、容错能力的定义 在副本集中,容错能力是指系统能够容忍多少个节点故障而仍然能够保持服务可用性的能力。这通常与选举机制中的多数投票原则密切相关。 二、三节点副本集的容错能力 在三节点的副本集中,通常有一个主节点和两个从节点。当主节点故障…...
基于 WEB 开发的手机销售管理系统设计与实现内容
标题:基于 WEB 开发的手机销售管理系统设计与实现 内容:1.摘要 摘要:随着智能手机的普及和电子商务的快速发展,手机销售行业面临着越来越多的挑战和机遇。为了提高销售效率和管理水平,本文设计并实现了一个基于 WEB 的手机销售管理系统。该系…...
LeetCode - Google 大模型校招10题 第1天 Attention 汇总 (3题)
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/145368666 GroupQueryAttention(分组查询注意力机制) 和 KVCache(键值缓存) 是大语言模型中的常见架构,GroupQueryAttention 是注意力…...
Vue3 provide/inject用法总结
1. 基本概念 provide/inject 是 Vue3 中实现跨层级组件通信的方案,类似于 React 的 Context。它允许父组件向其所有子孙组件注入依赖,无论层级有多深。 1.1 基本语法 // 提供方(父组件) const value ref(hello) provide(key, …...
Linux——网络基础(1)
文章目录 目录 文章目录 前言 一、文件传输协议 应用层 传输层 网络层 数据链路层 数据接收与解封装 主机与网卡 数据传输过程示意 二、IP和MAC地址 定义与性质 地址格式 分配方式 作用范围 可见性与可获取性 生活例子 定义 用途 特点 联系 四、TCP和UDP协…...
【记录】日常|从零散记录到博客之星Top300的成长之路
文章目录 shandianchengzi 2024 年度盘点概述写作风格简介2024年的创作内容总结 shandianchengzi 2024 年度盘点 概述 2024年及2025年至今我创作了786即84篇文章,加上这篇就是85篇。 很荣幸这次居然能够入选博客之星Top300,这个排名在我之前的所有年份…...
【二分查找】力扣373. 查找和最小的 K 对数字
给定两个以 非递减顺序排列 的整数数组 nums1 和 nums2 , 以及一个整数 k 。 定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2 。 请找到和最小的 k 个数对 (u1,v1), (u2,v2) … (uk,vk) 。 示例 1: 输入: nums1 [1,7,11], nums2 …...
池化层Pooling Layer
1. 定义 池化是对特征图进行的一种压缩操作,通过在一个小的局部区域内进行汇总统计,用一个值来代表这个区域的特征信息,常用于卷积神经网络(CNN)中。 2. 作用 提取代表性信息的同时降低特征维度,具有平移…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
