ESP32控制舵机实现自动开关灯

这是第四次把电子制作捡起来又放下,捡起来是因为刚刚搬了校区,感觉可以收拾一下宿舍,实现自动化开关电灯;放下是因为只完成了电路部分的制作,机械方面自己没有知识储备,自己做不出来了。😅😥😭

概述

这次项目其实很简单,B站上也有很多相似的项目。ESP32驱动舵机,舵机固定在电灯开关上,从而实现开灯。将ESP32通过Blinker接入天猫精灵,从而实现了语音唤醒功能。

image-20220907201340790

demo_bb

demo_pcb

demo_图示

制作

版本1 - 网页端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#include <ESP32Servo.h>
#include <WiFi.h>

Servo myservo; // 定义Servo对象来控制
int pos = 90; // 角度存储变量


// WIFI信息
const char* ssid = "ssid";
const char* password = "passwrod";

// Set web server port number to 80
WiFiServer server(80);

// 变量来存储HTTP请求
String header;

// 用于存储当前输出状态的辅助变量
String output26State = "off";
String output27State = "off";

// 配置输出引脚号
const int output26 = 26;
const int output27 = 27;

// 设置静态IP信息(配置信息前需要对将要接入的wifi网段有了解)
IPAddress local_IP(192, 168, 1, 111);
// 设置静态IP网关
IPAddress gateway(192, 168, 1, 1);

IPAddress subnet(255, 255, 0, 0);
IPAddress primaryDNS(8, 8, 8, 8); //optional
IPAddress secondaryDNS(8, 8, 4, 4); //optional

void kaideng(){
myservo.write(pos);
delay(2000);
myservo.write(pos+45);
delay(1000);
myservo.write(pos);
delay(2000);
}

void guandeng(){
myservo.write(pos);
delay(2000);
myservo.write(pos-45);
delay(1000);
myservo.write(pos);
delay(2000);
}


void setup() {

myservo.attach(16); // 控制线 GPIO16

Serial.begin(115200);
// Initialize the output variables as outputs
pinMode(output26, OUTPUT);
pinMode(output27, OUTPUT);
// Set outputs to LOW
digitalWrite(output26, LOW);
digitalWrite(output27, LOW);

// Configures static IP address
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
Serial.println("STA Failed to configure");
}

// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}



void loop(){
WiFiClient client = server.available(); // Listen for incoming clients

if (client) { // If a new client connects,
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();

// turns the GPIOs on and off
if (header.indexOf("GET /26/on") >= 0) {
Serial.println("GPIO 26 on");
output26State = "on";
digitalWrite(output26, HIGH);
} else if (header.indexOf("GET /26/off") >= 0) {
Serial.println("GPIO 26 off");
output26State = "off";
digitalWrite(output26, LOW);
} else if (header.indexOf("GET /27/on") >= 0) {
Serial.println("GPIO 27 on");
output27State = "on";
digitalWrite(output27, HIGH);
} else if (header.indexOf("GET /27/off") >= 0) {
Serial.println("GPIO 27 off");
output27State = "off";
digitalWrite(output27, LOW);
}

// 访问显示页面
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<meta charset=\"UTF-8\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS to style the on/off buttons
// Feel free to change the background-color and font-size attributes to fit your preferences
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button2 {background-color: #555555;}</style></head>");

// Web Page Heading
client.println("<body><h1>ESP32 Web Server</h1>");

// Display current state, and ON/OFF buttons for GPIO 26
client.println("<p>GPIO 26 - 状态 " + output26State + "</p>");
// If the output26State is off, it displays the ON button
if (output26State=="off") {
client.println("<p><a href=\"/26/on\"><button class=\"button\">开</button></a></p>");
} else {
client.println("<p><a href=\"/26/off\"><button class=\"button button2\">关</button></a></p>");
}

// Display current state, and ON/OFF buttons for GPIO 27
client.println("<p>GPIO 27 - 状态 " + output27State + "</p>");
// If the output27State is off, it displays the ON button
if (output27State=="off") {
client.println("<p><a href=\"/27/on\"><button class=\"button\">开</button></a></p>");
} else {
client.println("<p><a href=\"/27/off\"><button class=\"button button2\">关</button></a></p>");
}
client.println("</body></html>");

// The HTTP response ends with another blank line
client.println();
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header = "";
// Close the connection



client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
if (digitalRead(output26) == HIGH){kaideng(); Serial.println("已开灯");}
if (digitalRead(output27) == HIGH){guandeng();Serial.println("已关灯");}
}

版本2 - 接入天猫精灵

使用点灯科技Blinker

参考资料:

点灯科技 (diandeng.tech)

blinker (github.com)

blinker-iot/blinker-library: An IoT Solution,Blinker library for embedded hardware. Works with Arduino, ESP8266, ESP32. (github.com)

Release upgrade v0.3.7 · blinker-iot/blinker-library (github.com)

基本功能展示_哔哩哔哩_bilibili

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// 天猫精灵控制舵机实现开关灯
// 5V-红线
// gnd-棕线
// GPIO16-橙

// ssd1306
// 3.3v-vcc
// gnd-gnd
// GPIO21-scl
// GPIO22-sda


#define BLINKER_WIFI
#define BLINKER_ALIGENIE_MULTI_OUTLET

#include <Blinker.h>
#include <ESP32Servo.h>
#include <Wire.h>
#include "SSD1306.h"

#define SDA 21
#define SCL 22
// SSD 引脚设置
SSD1306 display(0x3c, SDA, SCL);


Servo myservo; // 定义Servo对象来控制
int pos = 90; // 角度存储变量


// 定义开关灯函数
void kaideng(){
myservo.write(pos);
delay(2000);
myservo.write(pos+45);
delay(1000);
myservo.write(pos);
delay(2000);
}

void guandeng(){
myservo.write(pos);
delay(2000);
myservo.write(pos-45);
delay(1000);
myservo.write(pos);
delay(2000);
}


char auth[] = "从点灯科技获取";
char ssid[] = "ssid";
char pswd[] = "pswd";

bool oState[5] = { false };

void aligeniePowerState(const String & state, uint8_t num)
{
BLINKER_LOG("need set outlet: ", num, ", power state: ", state);

if (state == BLINKER_CMD_ON) {
digitalWrite(LED_BUILTIN, HIGH);

BlinkerAliGenie.powerState("on", num);
kaideng();
BlinkerAliGenie.print();

oState[num] = true;
}
else if (state == BLINKER_CMD_OFF) {
digitalWrite(LED_BUILTIN, LOW);

BlinkerAliGenie.powerState("off", num);
guandeng();
BlinkerAliGenie.print();

oState[num] = true;

if (num == 0)
{
for (uint8_t o_num = 0; o_num < 5; o_num++)
{
oState[o_num] = false;
}
}
}
}

void aligenieQuery(int32_t queryCode, uint8_t num)
{
BLINKER_LOG("AliGenie Query outlet: ", num,", codes: ", queryCode);

switch (queryCode)
{
case BLINKER_CMD_QUERY_ALL_NUMBER :
BLINKER_LOG("AliGenie Query All");
BlinkerAliGenie.powerState(oState[num] ? "on" : "off", num);
BlinkerAliGenie.print();
break;
case BLINKER_CMD_QUERY_POWERSTATE_NUMBER :
BLINKER_LOG("AliGenie Query Power State");
BlinkerAliGenie.powerState(oState[num] ? "on" : "off", num);
BlinkerAliGenie.print();
break;
default :
BlinkerAliGenie.powerState(oState[num] ? "on" : "off", num);
BlinkerAliGenie.print();
break;
}
}

void dataRead(const String & data)
{
BLINKER_LOG("Blinker readString: ", data);

Blinker.vibrate();

uint32_t BlinkerTime = millis();

Blinker.print("millis", BlinkerTime);
}

void setup()
{
Serial.begin(115200);
BLINKER_DEBUG.stream(Serial);

pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);

Blinker.begin(auth, ssid, pswd);
Blinker.attachData(dataRead);

BlinkerAliGenie.attachPowerState(aligeniePowerState);
BlinkerAliGenie.attachQuery(aligenieQuery);

// 设置舵机信号线
myservo.attach(16); // GPIO16
ssdinit();
}

void ssdinit(){

// 设置OLED显示的内容
// 128*64
display.init();

display.setFont(ArialMT_Plain_10);
display.drawString(0, 0, "Hello World");

display.setFont(ArialMT_Plain_16);
display.drawString(0, 10, "Hello World");

display.setFont(ArialMT_Plain_24);
display.drawString(0, 25, "Hello World");

display.display();
}

void loop()
{
Blinker.run();
}

放弃

不知道怎么固定到电灯的开关上,考虑使用化学胶水或热熔胶,但均具有不可复用性,方案被否决。于是想学一下3D建模!

软件:Autodesk Fusion 360

参考教程:

如何正当免费地获取Fusion 360建模软件_哔哩哔哩_bilibili

Autodesk Fusion 360 一小时入门 (录播)_哔哩哔哩_bilibili

建模太无聊了,暂时先放弃!脱实向虚,我还是继续写程序去吧!

参考资料

Fritzing添加新的元件库的方法_飘飘花吹雪的博客-CSDN博客_fritzing元件库不见了

https://forum.fritzing.org/t/fritzing-part-of-an-esp32/5355/3

其余参考资料在正文中


ESP32控制舵机实现自动开关灯
https://cosmicdusty.cc/post/Ideas/ESP32ControlsServo/
作者
Murphy
发布于
2022年9月6日
许可协议