在上一篇关于Esp32的文章中,我们通过Esp32的蓝牙系统控制了家里的灯光。结尾处提到,这套蓝牙方案似乎有点耗电,于是乎我采取了wifi这个方案解决。
大概讲一下原理和思路:通过服务器提交表单,更改数据库参数。另一边,单片机不停检测数据库特定参数数据,若发生改变,则执行相对应的命令。(我设置的参数 0 为关灯 1 为开灯)
Ⅰ.方案需求
- 完整的控制设备(如果你还没有看上一期蓝牙版的教程,先去看完再继续哦)
- 一个迷你服务器+数据库(只要是能任何时候都在浏览器能访问的网页即可)
Ⅱ.网络服务的搭建
如果你已经准备好一个能随时访问的网页和数据库,那么我们就开始配置吧!
一、在数据库中建立表
登录后台数据库,新建表名为 home_intellenges 的表,分别添加 id 和 value 字段(表名若要更改请根据后面的提示更改源码)


二、上传控制页面到你的网站
一共有三个页面,分别是:
- index.html 远程控制器网页 用于控制灯的开和关
- upload_db.php 用于将我们的操控数据上传到数据库
- get_data.php 用于单片机请求并返回灯的开关状态数据
在你的后台新建一个文件夹,并将这三个文件上传上去(文件名不能改 除非自行改源码)
1.index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>岩石的智能家居</title>
<style>
body {
background-color: rgb(173, 166, 166);
}
div.my_room {
background-color: dodgerblue;
text-align: center;
position: absolute;
margin: auto;
left: 0;
right: 0;
top: 0;
bottom: 0;
width:300px;
height:150px;
border-radius: 100px;
box-shadow: 0 0 100px 42px white;
}
.light1 {
color: white;
}
.confirm_light1 {
position: relative;
top: 20px;
width: 60px;
height: 30px;
border-radius: 190px;
}
</style>
</head>
<body>
<form action="upload_db.php" method="post">
<div class="my_room">
<p class="light1">灯光控制1</p>
<span class="light1">开:</span><input type="radio" name="house_light" value="1">
<span class="light1">关:</span><input type="radio" name="house_light" value="0"><br>
<button type="submit" class="confirm_light1">确定</button>
</div>
</form>
</body>
</html>2.upload_db.php
<html>
<body>
<?php
$host = "localhost"; // 主机地址
$db_admin = "sql_admin"; // 数据库账户名
$db_name = "sql_name"; // 数据库名
$db_passwd = "sql_pwd"; // 数据库登录密码
// 数据表名 home_intellenges 若你创建的不是这个名称,请将所有的 home_intellenges 都换 成你的
// 定义变量并设置为空值
$house_light = "";
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$house_light = server_input($_POST["house_light"]);
// strcmp 二进制安全字符串比较
if (strcmp($house_light, "1") == 0) {
echo "开灯!";
turn_on();
} else {
turn_off();
echo "关灯!";
}
}
// 开灯
function turn_on() {
$conn=mysqli_connect($host, $db_name, $db_passwd);
if($conn){
$select = mysqli_select_db($conn,$db_name);
if($select) {
$sql = "update home_intellenges set value = 1 where id = 1";
$ok = mysqli_query($conn,$sql);
if($ok) {
header("location:index.html");
}
else {
echo "数据库修改错误";
}
}
else {
echo "表文件链接错误";
}
}
else {
echo "无法连接数据库";
}
}
// 关灯
function turn_off() {
$conn=mysqli_connect($host, $db_name, $db_passwd);
if($conn){
$select = mysqli_select_db($conn,$db_name);
if($select) {
$sql = "update home_intellenges set value = 0 where id = 1";
$ok = mysqli_query($conn,$sql);
if($ok) {
header("location:index.html");
}
else {
echo "数据库修改错误";
}
}
else {
echo "表文件链接错误";
}
}
else {
echo "无法连接数据库";
}
}
function server_input($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data, ENT_QUOTES);
return $data;
}
?>
</body>
</html>请根据文件开头的提示将数据录入完全,之后进入下一步。
3.get_data.php
<?php
$host = "localhost"; // 主机地址
$db_admin = "sql_admin"; // 数据库账户名
$db_name = "sql_name"; // 数据库名
$db_passwd = "sql_pwd"; // 数据库登录密码
// 数据表名 home_intellenges 若你创建的不是这个名称,请将所有的 home_intellenges 都换 成你的
class CallBackData
{
public $my_house_light1;
}
// 定义变量并设置为空值
$http_data = "";
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$http_data = server_input($_POST["house_light"]);
// strcmp 二进制安全字符串比较
// 接收 house_light: state
if (strcmp($http_data, "state") == 0) {
return_func();
}
else {
echo "error code: 1";
}
} else if ($_SERVER["REQUEST_METHOD"] == "GET") {
$http_data = server_input($_GET["house_light"]);
// strcmp 二进制安全字符串比较
// 接收 house_light: state
if (strcmp($http_data, "state") == 0) {
return_func();
}
else {
echo "error code: 2";
}
} else {
echo "error code: 3";
}
function return_func()
{
$data = new CallBackData(); // 创建返回数据对象
$conn=mysqli_connect($host, $db_name, $db_passwd);
if($conn) {
$select = mysqli_select_db($conn,$db_name);
if($select) {
$sql = "SELECT * FROM `home_intellenges` WHERE id = 1"; // 数据库执行语句 home_intellenges 为搜索的表名
$ok = mysqli_query($conn, $sql);
if($arr=mysqli_fetch_array($ok)) {
$data->my_house_light1 = "".$arr['value']."";
echo json_encode($data); // 返回JSON数据
}
else {
echo "ERROR: cannot locate data";
}
}
}
}
function server_input($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data, ENT_QUOTES);
return $data;
}
?>三、连接测试
通过输入 你的域名/文件路径/index.html 访问刚才上传上去的页面 查看是否成功

当你的页面出现上述画面的时候,恭喜你成功了!(请不要吐槽我的网页设计,只是随便弄的 想要弄得好看点可以自己修改 index.html 代码)
你可以通过提交开和关命令看看你数据库中的value值是否发生变化,如果是,那么恭喜你,可以进入下一步了。
Ⅲ.Esp32代码烧录
万事俱备,只欠东风!像上次那样,将我们的Esp32连接至电脑,开始烧录代码
废话不多说,直接上完整代码:
#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
// 请根据提示自行更改下面函数: WiFi.begin(); 里面的WIFI信息
// 请根据提示自行更改下面函数: http.begin(); 里面的内容
// 如果觉得响应的速度太慢,可以通过修改本页最后一个delay()函数的参数提高速度(默认5秒检测一次)
HTTPClient http;
// 舵机通道
int channel_PWM = 3;
// 舵机频率,那么周期也就是1/50,也就是20ms ,PWM一共有16个通道,0-7位高速通道由80Mhz时钟驱动,后面8个为低速通道由1Mhz时钟驱动
int freq_PWM = 50;
// PWM分辨率,取值为 0-20 之间 ,这里填写为10,那么后面的ledcWrite 这个里面填写的pwm值就在 0 - 2的10次方 之间 也就是 0-1024 ,如果是要求不高的东西你可以直接拿1000去算了
int resolution_PWM = 10;
// 绑定的IO,在下面的绑定函数里面会用到,绑定之后这个IO就会变成我们PWM的输出口
const int PWM_Pin = 4; //指定pwm绑定到这个io上输出
#define PMW_EN 0
const int Motor_PWM_PinB = 4;
int interruptCounter = 0;
hw_timer_t *timer = NULL;
void IRAM_ATTR TimerEvent()
{
interruptCounter++;
// Serial.println(interruptCounter++);
if (interruptCounter > 5)
{
interruptCounter = 1;
}
// Serial.println(ledcRead(5));
}
void WiFi_Connect()
{
WiFi.begin("wifi_account", "password"); // 在这里输入你区分大小写的wifi名称和密码
while (WiFi.status() != WL_CONNECTED)
{ //这里是阻塞程序,直到连接成功
delay(300);
Serial.print(".");
}
}
void setup()
{
Serial.begin(115200);
WiFi.mode(WIFI_STA); //设置为STA模式
WiFi.disconnect(); //断开当前可能的连接
delay(1000);
// 初始化电机
ledcSetup(channel_PWM, freq_PWM, resolution_PWM); // 设置舵机通道
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &TimerEvent, true);
timerAlarmWrite(timer, 1000000, true);
timerAlarmEnable(timer); // 使能定时器
pinMode(2, OUTPUT);
digitalWrite(2, HIGH);
WiFi_Connect(); // 尝试连接wifi
// 灯灭表示wifi连接成功
digitalWrite(2, LOW);
}
int flag = 1; // 记录灯的状态
void loop()
{
if (WiFi.status() == WL_CONNECTED)
{
http.begin("在这里输入get_data.php的完整路径之后再后面加上?house_light=state"); // 例如 http://littlerook.top/light/get_data.php?house_light=state
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
String pageData = http.getString(); // 获取页面
StaticJsonDocument<200> doc;
deserializeJson(doc, pageData); // 解析json文件包
int callback = doc["my_house_light1"];
// Serial.println(callback); // 获取内容
if (callback == 1 && callback != flag) {
Serial.println("LIGHT_ON");
delay(100);
ledcAttachPin(PWM_Pin, channel_PWM); //将 LEDC 通道绑定到指定 IO 口上以实现输出
ledcWrite(channel_PWM, 101); //86
delay(300);
ledcWrite(channel_PWM, 77);
delay(100);
ledcDetachPin(PWM_Pin); //这个是解除IO口的pwm输出功能模式
flag = 1;
}
if (callback == 0 && callback != flag) {
Serial.println("LIGHT_OFF");
delay(100);
ledcAttachPin(PWM_Pin, channel_PWM); //将 LEDC 通道绑定到指定 IO 口上以实现输出
ledcWrite(channel_PWM, 57);
delay(300);
ledcWrite(channel_PWM, 77);
delay(100);
ledcDetachPin(PWM_Pin); //这个是解除IO口的pwm输出功能模式
flag = 0;
}
}
else
{
digitalWrite(2, HIGH);
WiFi_Connect(); // 尝试连接wifi
// 灯灭表示wifi连接成功
digitalWrite(2, LOW);
}
http.end();
}
delay(5000); // 每5秒运行一次
}
请按照代码行前面的提示修改特定位置的参数 上传后连好电线,理论上你的Esp32-Wifi控制版已经完成了!接下来只要你家WIFI不断,你在任何地方都能控制你家的灯!
Ⅳ.小结
细心的小伙伴已经发现了,这个方案还是有些许问题,比如当我们手动关闭开关的时候,下次还需要先往反方向打一次,再开灯,这样会浪费一点时间,我的解决办法是将检测延迟降低,降低等待成本,除此之外,暂时想不到其他更好的方法。
另外如果想增加多几个控制的灯,这就需要大家按照源码增加代码就可以了,注释也写得很明白了,如果有不明白的可以在评论区问我哦,我看到都会回复的。
但是总的来讲,这个方案已经比较完善,大家在享受科技带来的进步的时候,请不要得意地将控制地址分享给你的“好”基友!否则半夜灯闪个不停就能怪我了,总不能增加个登录验证吧!




Comments | NOTHING