从零开始的DIY智能家居 - 基于 ESP32 的智能浇水器
前言
上次 土壤湿度传感器 完成之后,就立下一个 flag 要搭建一个智慧浇水的智能场景,现在终于有时间填坑了!(o゚▽゚)o
智慧浇水场景的核心设备有三个:
检测土壤状态的:土壤湿度传感器 通过这个传感器来获取土壤信息,作为是否浇水的依据。
智能浇水器:执行装置,通过 Spirit 1 控制。
Spirit 1
这次就来制作智慧浇水的智能场景的核心: 智能浇水器,我准备买一个便宜的傻不拉几的浇水器自己改造一下,想办法给他连上脑子。
主要交互流程如下图:
(σ゚∀゚)σ..:*☆哎哟不错哦,是不是很厉害啊!
硬件选择
万年不变的 安信可的 ESP32S ,别问,问就是便宜才 24元。
继电器,因为不清楚浇水器电路情况保险起见,使用了继电器进行隔离,4.5元
浇水器 淘宝随便找的 99元,选择它是因为这个方便改造,有一个可以拆卸的电池盒方便塞开发板和继电器,按钮是机械式的,可以通过继电器短接模拟按钮效果进行控制,并且有一个手动浇水的功能,也就是按钮摁一下就浇水,再摁一下就关闭,我们从这个功能下手。
(写文章的时候这东西已经被我拆掉了,就拿淘宝的图凑活一下吧,图上按的中间按钮就是我们需要接管的按钮)
(((((((((((っ•ω•)っ Σ(σ`•ω•´)σ 起飞!
改造接线
硬件都到了之后就开始改造电路!
控制电路:
浇水器面板中间的按钮就是手动控制按钮下降沿触发,而我们在这里使用了一个继电器常开端接到按钮上,当开发板 12号 IO 口给继电器电压时,继电器常开端闭合,按钮被短接,两端电压被拉至5V,0.1S后断开,电压拉低,下降沿触发。
休眠检测电路:
浇水器中有一个10S左右没有控制就进入休眠状态的设置我们没办法修改,进入休眠状态后需要一个额外的触发来唤醒浇水器,而浇水器唤醒时,会点亮数码管,于是就通过 A0 引脚接到数码管的共阳级,如果检测到数码管的共阳级为低电平,就认为浇水器进入休眠状态,在触发命令之前额外触发一次,解除浇水器的休眠状态。
浇水器工作状态检测电路:
浇水器面板通过信号线来控制下面水泵电机的工作,这里我通过5号 IO 监控信号线的电压来确定电机的工作状态。
代码解析
为了方便讲解逻辑,我会打乱代码的顺序可能还会进行裁剪,要是想直接拿代码跑的朋友可以直接去 灵感桌面的秘密宝库 获取代码,或者直接 clone:
https://gitee.com/inspiration-desktop/DEV-lib-arduino.git
要是连 git 是什么都不知道,可以参考简单无脑,上手即用 - 手把手教你使用 智能红外温度传感器代码以及依赖的 gitee 库!
下载或者 clone 代码后这次用到的是这个三个文件夹:
cjson:我移植的 cjson 库,就是标准的 cjson 库,放到 arduino 安装目录下的 libraries 文件夹里,百度一下 cjson 的函数使用就行了。
libsddc:是我移植自官方的SDDC库和自己写的 SDK,也是放入 libraries 文件夹里就行。里面是 SDDC 协议的处理函数,我们不用管。
demo 文件夹里面就是我们各种传感器的 demo 代码了:
具体 arduino 使用教程可以看我之前的文章 arduino开发指导 和 手把手带你 arduino 开发:基于ESP32S 的第一个应用-红外测温枪(带引脚图)
设备控制命令:
通过 Spirit 1 的应用程序或者 嗅探器 向传感器设备发送的命令:
通过向浇水器发送 "ON"/"OFF" 的 set 命令可以控制浇水器是否浇水:
{
"method": "set", // 控制浇水器开始/停止浇水
"watering": "ON"/"OFF"
}
通过向浇水器发送 "watering" 的 get 命令可以获取浇水器是否有在浇水:
{
"method": "get", // 获取浇水器工作状态
"obj": ["watering"]
}
设备和协议初始化流程:
基于官方 demo 写的不需要做什么修改,主要是设备初始化,管脚配置,和协议初始化部分。
因为涉及到 IO 口的输入和输出,所以需要手动配置一下 IO 口状态。并且创建一个一个消息队列来储存和传递收到的命令
void sensor_init()
{
pinMode(water_pin, OUTPUT);
pinMode(sign_pin, INPUT);
pinMode(monitor_pin,INPUT);
// 设置一个消息队列来缓存命令,防止命令丢失
Message_Queue = xQueueCreate(MESSAGE_Q_NUM, MESSAGE_REC_LEN); //创建消息Message_Queue
if(Message_Queue == 0)
{
printf("队列 Message_Queue 创建失败!\r\n");
}
}
void setup() {
// 这部分主要是协议初始化和设备初始化,没有需要修改的地方,详见gitee库
}
void loop() {
// 这部分主要是协议初始化和设备初始化,没有需要修改的地方,详见gitee库
}
配置设备信息
这部分代码可以配置 WiFi 名字和 WiFi 密码,要使用的引脚,并且配置设备在 Spirit 1 上显示的信息:
// 依赖度头文件和库
#include "Arduino.h"
#include <OneButton.h>
#include <WiFi.h>
#include <sddc.h>
#include <cJSON.h>
#include <Wire.h>
#include <SDDC_SDK_lib.h>
#define SDDC_CFG_PORT 680U // SDDC 协议使用的端口号
#define PIN_INPUT 0 // 选择 IO0 进行控制
#define ESP_TASK_STACK_SIZE 4096
#define ESP_TASK_PRIO 25
#define MESSAGE_Q_NUM 5 // 数据的消息队列的数量
#define MESSAGE_REC_LEN 5 // 数据的消息队列的长度
static sddc_t *g_sddc;
static const char* ssid = "TP-LINK_54F9C2"; // WiFi 名
static const char* password = "1234567890"; // WiFi 密码
static const int water_pin = 12; // 浇水器的控制引脚,控制浇水器启停
static const int sign_pin = A0; // 浇水器的状态监视引脚,查看浇水器是否休眠
static const int monitor_pin = 5; // 工作状态监视引脚,监视浇水器启停
QueueHandle_t Message_Queue;
static int xTicksToDelay = 5000; // 周期延时时间
OneButton button(PIN_INPUT, true);
这里填写设备的信息,方便在 Spirit 1 上查看和寻找你需要的设备:
/*
* 当前设备的信息定义
*/
DEV_INFO dev_info = {
.name = "智能浇水", // 设备的名字
.type = "device",
.excl = SDDC_FALSE,
.desc = "ESP-32S",
.model = "1",
.vendor = "inspiration-desktop",
};
回调函数注册
这是收到命令后回调函数注册的位置,在这里注册的函数才能被 SDK 正确的调用,执行正确的动作。
因为浇水器 set 命令为 string 类型,所以对应的处理函数 water_set 注册到 IO设备对象设置函数与处理方法注册 中。
/*
* IO设备对象设置函数与处理方法注册
*/
IO_DEV_REGINFO io_dev[] = {
{"watering",water_set},
};
而 get 处理函数返回的同样是 string 类型,所以在 系统对象状态获取注册 中第二个参数选择 DEV_IO_TYPE,并且注册 get 处理函数 single_get_sensor。
/*
* 系统对象状态获取注册
*/
DEV_STATE_GET dev_state_get_reg[] = {
{"watering", DEV_IO_TYPE, single_get_sensor},
};
具体 SDK 的解析可以参考 同人逼死官方系列!基于sddc 协议的SDK框架 sddc_sdk_lib 解析 和 同人逼死官方系列!从 DDC 嗅探器到 sddc_sdk_lib 的数据解析
数据获取与发送流程
这里是自己编写的处理流程 ,可以根据需求自己更改,收到 set 或者 get 后上文注册的函数,进入对应的处理函数。
收到 set 命令后,通过关键字寻找到对应的处理函数 water_set ,判断命令是否正确(比如说正在浇水的时候,收到一个ON命令),检测浇水器是否休眠,如果休眠了那在触发前就唤醒设备。
而收到 get 命令后进入对应的处理函数 single_get_sensor 通过读取面板信号线判断电机工作状态,并且返回给 Spirit 1。
/*
* 主动数据上报函数
*/
static void report_sensor()
{
int sensorValue = 0;
cJSON *value;
cJSON *root;
value = cJSON_CreateArray();
root = cJSON_CreateObject();
sddc_return_if_fail(value);
sddc_return_if_fail(root);
// 按格式生成需要的数据
cJSON_AddItemToArray(value, cJSON_CreateString("上报数据 1 ")); // 这里的字符串要和系统对象状态获取注册结构体里的对应
// cJSON_AddItemToArray(value, cJSON_CreateString("上报数据 2 ")); // 需要上报几个就添加几个
cJSON_AddItemToObject(root, "obj", value);
// 发送数据给 EdgerOS
object_report(root);
cJSON_Delete(value);
}
/*
* 浇水状态监控函数
*/
static void monitor_task(void *arg)
{
int newval = 1;
int oldval = 1;
// 监控浇水开启和关闭状态
while(1)
{
newval = digitalRead(monitor_pin);
if(newval != oldval)
{
report_sensor();
}
oldval = newval;
// 任务创建之后,设定延时周期
delay(100);
}
vTaskDelete(NULL);
}
/*
* 浇水触发任务
*/
static void button_task(void *arg)
{
char SW[5];
char *value;
BaseType_t err;
if(Message_Queue != NULL)
{
err = xQueueReceive(Message_Queue, &value, portMAX_DELAY );
if(err == pdFALSE)
{
printf("队列 Message_Queue 数据获取失败!\r\n");
}
}
sddc_printf("\nMessage_Queue value: %s!!!!!\n", value);
// 监控电机工作状态
if(digitalRead(monitor_pin))
{
strcpy(SW,"OFF");
}else
{
strcpy(SW,"ON");
}
// 如果命令要求与电机当前工作状态一致就不处理
if(0 != strcmp(value,SW) && (value != NULL))
{
// 判断机器是否休眠如果休眠了就行唤醒机器
delay(100);
int a = analogRead(sign_pin);
sddc_printf("\n a1 == : %d!!!!!\r\n", analogRead(sign_pin));
if(!(a > 1) && (0 == strcmp(value,"ON")))
{
Serial.println("唤醒");
digitalWrite(water_pin, HIGH);
delay(100);
digitalWrite(water_pin, LOW);
delay(100); // 因为是下降沿触发,所以加延迟保证下降沿不会被后面的命令冲掉
}
// 触发浇水器开或者关
Serial.println("触发");
digitalWrite(water_pin, HIGH);
delay(100);
digitalWrite(water_pin, LOW);
delay(100);
}
vTaskDelete(NULL);
}
/*
* 浇水器控制函数
*/
sddc_bool_t water_set(const char* value)
{
BaseType_t err;
sddc_printf("\niot_pi_on_message: %s!!!!!\n", value);
if((Message_Queue != NULL)&&(value))
{
// 通过消息队列储存收到的命令,防止命令丢失
err = xQueueSendToFront(Message_Queue,&value,0 );
if(err == pdFALSE)
{
printf("队列 Message_Queue 已满,数据发送失败!\r\n");
}
}
// 创建电机触发任务,防止阻塞message_ack回复
xTaskCreate(button_task, "button_task", ESP_TASK_STACK_SIZE, NULL, ESP_TASK_PRIO, NULL);
return SDDC_TRUE;
}
/*
* 填写浇水状态
*/
sddc_bool_t single_get_sensor(char *objvalue, int value_len) // 注意函数名要和上文注册的函数名保持一致,当收到 get 消息之后通过关键字就能找到并且调用这个函数
{
if(digitalRead(monitor_pin))
{
strncpy(objvalue, "OFF", value_len);
}else
{
strncpy(objvalue, "ON", value_len);
}
return SDDC_TRUE;
}
代码写完之后烧录进去就完事了,和之前完全一样,点一下保存,然后上传OK,具体可以看之前的文档,我就懒得再写一遍啦 (/ω\)。
总结
智能浇水器制作完成!加上之前制作的土壤湿度传感器,和 Spirit 1 就完成了我们智能浇花场景的搭建。接下来就写一个智能浇花的应用就能完美的解决忘记浇水的麻烦!
从零开始的DIY智能家居 - 基于 ESP32 的智能浇水器的更多相关文章
- 从零开始的DIY智能家居 - 基于 ESP32 的智能水浊度传感器
前言 家里有个鱼缸养了几条鱼来玩玩,但是换水的问题着实头疼,经常一个不注意就忘记换水,鱼儿就没了.o(╥﹏╥)o 在获得 Spirit 1 边缘计算机 后就相当于有了一个人智能设备服务器,可以自己开发 ...
- 从零开始 DIY 智能家居 - 基于 ESP32 的智能紫外线传感器模块
目录 前言 硬件选择 二.使用步骤 获取代码 设备控制命令: 设备和协议初始化流程: 配置设备信息 回调函数注册 数据获取与上报流程 总结 前言 做了这么多传感器都是自己玩,这次家里人看不下去了,非得 ...
- 从零开始 DIY 智能家居 - 基于 ESP32 的智能语音合成播报模块
目录 前言 硬件选择 代码解析 获取代码 设备控制命令: 设备和协议初始化流程: 配置设备信息 回调函数注册 语音播报与设置流程 总结 前言 这里这么多设备,突然发现我做的好像都是传感器之类的居多好像 ...
- 从零开始的DIY智能家居 - 基于 ESP32 的智能光照传感器
前言 上周出差有点急,结果家里灯没关,开了整整一周的时间(T▽T),整个人都裂开了,准备做一个能够远程控制灯的东西,让我以后出差能远程把家里灯关了. 第一步就是做这期的主题 - 智能光照传感器,因为我 ...
- 从零开始的DIY智能家居 - 基于 ESP32 的土壤湿度传感器
前言 自从上次做了那个 甲醛传感器 和 水浊度传感器 之后开始尝到智能家居的甜头了,这两东西有没有用我不知道,但是没事的时候掏出手机瞄两眼,看着就让人很安心( ̄︶ ̄). 于是懒惰的我开始琢磨把给植物浇 ...
- 基于ESP32的智能家居管理系统的设计与实现
基于ESP32的智能家居管理系统的设计与实现 ESP32的智能家居管理系统访问链接: https://www.cnblogs.com/easyidea/p/13101165.html 一.需求分析 1 ...
- 手把手搭建自己的智能家居 - 基于 IOT Pi 的智能甲醛检测器
智慧家居 - 基于 IOT Pi 的智能甲醛检测器 之前的文章体验 MS-RTOS 的时候入手了一个块 IOT Pi ,放着也是浪费,这次我们就利用 IOT PI 开发一个智能甲醛检测器.φ(> ...
- 基于能量收集的智能家居-2013国家级大学生创业实践项目申报_商业计划书_V0.2
SmartHome项目商业计划 基于能量收集的 免电池无线智能家居系统 IA-SmartHome团队 2012.12 l 基于无线的智能家居解决方案,节省施工成本: l 基于能 ...
- 谁才是智能家居的未来?视声M+O融合方案给出答案
有些智能家居,你不能说它不智能. 但在现实生活中,常常帅不过一秒. 就比如,当你经历了一天的疲惫后回到家,发现玄关的智能开关突然没反应,家里的灯怎么都打不开.这种时候你得明白,你的智能开关面板很有可能 ...
随机推荐
- Docker 容器间的单向连接
Docker 容器间的单向连接 前言 a. 本文主要为 Docker的视频教程 笔记. b. 环境为 CentOS 7.0 云服务器 c. 上一篇:Dockerfile 自动制作 Docker 镜像( ...
- 496. 下一个更大元素 I
496. 下一个更大元素 I 给定两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集.找到 nums1 中每个元素在 nums2 中的下一个比其大的值. ...
- easyx小游戏
#include "stdafx.h" int main(){ srand(time(NULL)); initgraph(640,480); int user_x=20,user_ ...
- Java基础系列(24)- 增强for循环
增强for循环 这里我们先只是见一面,做个了解,之后数组部分会重点使用 Java5引入了一种主要用于数组或集合的增强型for循环 Java增强for循环语法格式如下 for(声明语句:表达式){ // ...
- windom 下面redis安装和扩展安装
参考 https://www.cnblogs.com/yulongcode/p/10585229.html https://blog.csdn.net/qq_41921511/article/deta ...
- 一文让你掌握软件测试工程师SQL面试题
数据结构说明 已知有如下4张表: 学生表:student(学号,学生姓名,出生年月,性别) 成绩表:score(学号,课程号,成绩) 课程表:course(课程号,课程名称,教师号) 教师表:teac ...
- cgroup之cpu关键参数
cpu.cfs_period_us specifies a period of time in microseconds (µs, represented here as "us" ...
- 深入理解Python切片
Python序列的切片很基础同时也很重要,最近看到一个[::-1]的表达,不明所以,查了一些资料并实际操作,对Python切片有了更深刻的认识,以下结合例子详细说明.先看下切片的基本语法,一般认为切片 ...
- vue项目中的element-ui地区级联选择器加详细地址push到对象中不显示的问题
想要实现级联选择器el-cascader和输入框el-input共同组成的详细地址,添加数据时弹出el-drawer嵌套el-form弹窗,然后在el-form添加数据提交后push到el-table ...
- 华为Awareness kit,您旅途路上的超智能管家
前言 前段时间看了一部纪录片<中国游客在巴黎>,讲述了外国人眼中"中国式旅游":热衷景点打卡,沉迷拍照留念,无暇仔细欣赏:留足时间,买买买,不能枉此行.网友总结中国式旅 ...