|
本帖最后由 cih1996 于 2023-1-23 05:56 编辑
如果想脱离局域网(即不再同一个Wi-Fi)
那么就需要购买一个服务器作为中间件传输指令了。
这里我们必须为3部分讲解:
第1部分:硬件的对接
第2部分:服务器代码的逻辑设计
第3部分:ESP8266代码的实现
第4部分:Siri快捷指令设计
第1部分:硬件的对接
首先供电方法是用的双USB输出5V的充电宝; 分别给ESP8266和1路继电器进行供电。
无线遥控器是使用的R433X震晶模块,这里我直接选择对它的遥控按钮下手。
将开门的2个引脚点(按下时通电开门)用电线焊接在一起并接入继电器的COM和ON端,这样就可以通过继电器的闭合实现了模拟按下操作。
ESP8266这边接入的是D1引脚,到时候通过代码控制D1输出高电平,触发继电器闭合。
第2部分:服务器代码的逻辑设计
如果我们脱离局域网,不直接访问ESP8266模块进行控制D1引脚的话,就需要借助服务器。
1、首先需要设计数据库,存放设备的标志符,也可以说是唯一ID号(自己定义,方便多个设备的时候进行区别)。
2、然后设计出一个API接口,用于给Siri执行,这个接口的作用就是给设备做上开门的指令,存放到数据库里
3、在设计出一个API接口,用于给ESP8266模块读取开门指令(如果Siri执行了就有指令,如果没执行的话就没有)
4、基本上整个操作就这样了,Siri访问我们的接口把指令放到数据库,ESP8266访问我们的数据库读取到指令进行反应。
数据库我设计了2个表,1个是设备列表,1个是指令分配表
设备表:esp_device_list
4个主要字段:
1、device_name 设备的名字,方便我们观看。
2、device_id 设备的唯一ID,用于区分设备。
3、invo_count 设备执行指令的次数,便于统计。
4、heart_time 设备最后一次访问服务器的时间,用于方便我们判断是否还在线。
指令分配表:esp_device_command
1、device_id 设备的唯一ID
2、command 设备的指令(自己定义)
3、is_read 设备是否已经获取了指令(如果ESP8266读取到了指令,则改成1,避免二次读取)
4、callback_state 设备回调指令状态(某些特殊情况,我们需要知道ESP8266有没有成功的执行指令,如果成功了,可以通过改变这里的字段进行通知)
5、create_time 指令的创建时间(便于做日记查询)
下面分别是指令下发和指令读取的PHP代码:
以下是提供给Siri下发指令的API接口
以下是提供给ESP8266读取的API接口
另外为还扩展了一下额外的接口,方便进行后续的优化,简单设计了后台页面,便于可视化操作。
第3部分:ESP8266代码的实现
先来看看ESP8266的代码,用的是wifiinfo开发版:
- #include <ESP8266WiFi.h>
- #include <WiFiClient.h>
- #include <ESP8266WebServer.h>
- #include <ESP8266mDNS.h>
- #include <ESP8266HTTPClient.h>
- #include <ArduinoJson.h>
- #ifndef STASSID
- #define STASSID "Wi-Fi名字"
- #define STAPSK "Wi-Fi密码"
- #endif
- const char* ssid = STASSID;
- const char* password = STAPSK;
- String device_id = "A001";
- //saved wifi ssid and password file path
- String file_wifi_config = "/wifiInfo.config";
- DynamicJsonDocument JSON_Buffer(1*1024);
- ESP8266WebServer server(80);
- const int led = 13;
- void initWifiConnect(){
- File dataFile = SPIFFS.open(file_wifi_config,"r");
- String dataStr = dataFile.readString();
- dataFile.close();
- Serial.println(dataStr);
- WiFi.mode(WIFI_STA);
- WiFi.begin(ssid, password);
- Serial.println("");
- // Wait for connection
- while (WiFi.status() != WL_CONNECTED) {
- delay(500);
- Serial.print(".");
- }
- Serial.println("");
- Serial.print("Connected to ");
- Serial.println(ssid);
- Serial.print("IP address: ");
- Serial.println(WiFi.localIP());
- }
- //Show Wifi Config Page
- void handleWifiConfig(){
- String outHtml = "<!DOCTYPE html><html><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>配置无线网络</title></head><body> <div style="margin: 0;padding: 1rem;color: #ffffff;background-color: rgb(10,10,10);"> 请配置无线网络信息 </div> <form action="setWifi"> <p>无线网络:<input type="text" name="ssid" placeholder="请输入Wi-Fi名称"></p> <p>无线密码:<input type="text" name="password" placeholder="请输入Wi-Fi密码"></p> <p><input type="submit" value="提交链接" name=""></p> </form></body></html>";
- server.send(200, "text/html", outHtml);
- }
- //Recv User Input Wifi Config
- void handleWifiSet(){
- String ssid = server.arg("ssid");
- String password = server.arg("password");
- server.send(200, "text/plain", "connecting...");
- WiFi.begin(ssid, password);
- Serial.println("");
- Serial.print("Connected to ");
- Serial.println(ssid);
- Serial.print("IP address: ");
- Serial.println(WiFi.localIP());
- File dataFile = SPIFFS.open(file_wifi_config,"w");
- dataFile.println(ssid);
- dataFile.println(password);
- dataFile.close();
- }
- void handleNotFound() {
- digitalWrite(led, 1);
- String message = "File Not Found\n\n";
- message += "URI: ";
- message += server.uri();
- message += "\nMethod: ";
- message += (server.method() == HTTP_GET) ? "GET" : "POST";
- message += "\nArguments: ";
- message += server.args();
- message += "\n";
- for (uint8_t i = 0; i < server.args(); i++) {
- message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
- }
- server.send(404, "text/plain", message);
- digitalWrite(led, 0);
- }
- void setup(void) {
- pinMode(led, OUTPUT);
- pinMode(D1, OUTPUT);
- digitalWrite(led, 0);
- Serial.begin(115200);
- initWifiConnect();
- if (MDNS.begin("esp8266")) {
- Serial.println("MDNS responder started");
- }
- server.on("/", handleWifiConfig);
- server.on("/setWifi", handleWifiSet);
- server.on("/open", []() {
- digitalWrite(D1, 1);
- delay(1000);
- digitalWrite(D1, 0);
- server.send(200, "text/plain", "open success~!");
- });
-
- server.onNotFound(handleNotFound);
- /////////////////////////////////////////////////////////
- // Hook examples
- server.addHook([](const String & method, const String & url, WiFiClient * client, ESP8266WebServer::ContentTypeFunction contentType) {
- (void)method; // GET, PUT, ...
- (void)url; // example: /root/myfile.html
- (void)client; // the webserver tcp client connection
- (void)contentType; // contentType(".html") => "text/html"
- Serial.printf("A useless web hook has passed\n");
- Serial.printf("(this hook is in 0x%08x area (401x=IRAM 402x=FLASH))\n", esp_get_program_counter());
- return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
- });
- server.addHook([](const String&, const String & url, WiFiClient*, ESP8266WebServer::ContentTypeFunction) {
- if (url.startsWith("/fail")) {
- Serial.printf("An always failing web hook has been triggered\n");
- return ESP8266WebServer::CLIENT_MUST_STOP;
- }
- return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
- });
- server.addHook([](const String&, const String & url, WiFiClient * client, ESP8266WebServer::ContentTypeFunction) {
- if (url.startsWith("/dump")) {
- Serial.printf("The dumper web hook is on the run\n");
- // Here the request is not interpreted, so we cannot for sure
- // swallow the exact amount matching the full request+content,
- // hence the tcp connection cannot be handled anymore by the
- // webserver.
- #ifdef STREAMSEND_API
- // we are lucky
- client->sendAll(Serial, 500);
- #else
- auto last = millis();
- while ((millis() - last) < 500) {
- char buf[32];
- size_t len = client->read((uint8_t*)buf, sizeof(buf));
- if (len > 0) {
- Serial.printf("(<%d> chars)", (int)len);
- Serial.write(buf, len);
- last = millis();
- }
- }
- #endif
- // Two choices: return MUST STOP and webserver will close it
- // (we already have the example with '/fail' hook)
- // or IS GIVEN and webserver will forget it
- // trying with IS GIVEN and storing it on a dumb WiFiClient.
- // check the client connection: it should not immediately be closed
- // (make another '/dump' one to close the first)
- Serial.printf("\nTelling server to forget this connection\n");
- static WiFiClient forgetme = *client; // stop previous one if present and transfer client refcounter
- return ESP8266WebServer::CLIENT_IS_GIVEN;
- }
- return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
- });
- // Hook examples
- /////////////////////////////////////////////////////////
- server.begin();
- Serial.println("HTTP server started");
- }
- JsonObject root; /* 定义一个JSON对象的根节点root 用于获取将JSON字符串经过显示类型转换为JSON对象的报文主体 */
- int JsonCode;
- const char* JsonCommand = NULL;
- void loop(void) {
- if ((WiFi.status() == WL_CONNECTED)) {
- WiFiClient client;
- HTTPClient http;
- http.begin(client, "http://www.abc.com/api/device/heartbeat?device_id=" + device_id );
- http.addHeader("Content-Type", "application/json");
- int httpCode = http.GET();
- if(httpCode == HTTP_CODE_OK){
- const String& payload = http.getString();
- //decode Json String
- DeserializationError error = deserializeJson(JSON_Buffer, payload);
- if(error){
- Serial.println("deserializeJson JSON_Buffer is ERROR!!!");
- return ;
- }else{
- root = JSON_Buffer.as<JsonObject>();
- //Print Json Array
- //serializeJsonPretty(JSON_Buffer,Serial);
- //Get Json Key-Value
- JsonCode = JSON_Buffer["code"];
- Serial.print("code --> ");
- Serial.println(JsonCode);
- if(JsonCode == 0){
- JsonCommand = JSON_Buffer["command"];
- //execl open
- Serial.print("command --> ");
- Serial.println(JsonCommand);
- if(String(JsonCommand) == "open"){
- Serial.println("execl open");
- digitalWrite(D1, 1);
- delay(1000);
- digitalWrite(D1, 0);
- }
- }
- }
- Serial.println("received payload:\n<<");
-
-
- Serial.println(">>");
- }
- http.end();
- delay(2000);
- }
- server.handleClient();
- MDNS.update();
- }
复制代码
代码是引用自WifiServer的例子进行修改,所以大可不必直接复制粘贴,避免报错。
因为里面引用到了ArduinoJson库,需要在库管理里面安装。
这里重点说一下代码流程:
先设置好esp8266连接到你家里的Wi-Fi网络。
然后在loop处循环访问你的服务器接口,也就是刚刚设计的:/api/device/heartbeat 接口,用于返回指令的结果。
command就是由我们后面通过Siri自定义设置的,当我们给command设置为"open"时,以下图片所示的代码,读取到了open,则给D1输出电压,让继电器触发1秒时间在断开,因为是门,只需要模拟人工按一下就可以恢复了。
第4部分:Siri快捷指令设计
Siri的快捷指令,其实可以1行代码就可以实现。
我们添加获取URL内容指令
URL就输入 http://你的服务器IP/api/device/heartbeat?device_id=A001&command=open
最后把指令的名字改成"开门"。
这个时候你只需要说“嘿Siri,开门” 就能成功触发这个API接口。
当然我为了更智能,我还加了一个API接口,记得刚刚我们数据库有一个heart_time字段,这样我就可以先判断这个设备是不是还在线。如果不在线了就语音播报提醒,如果在线在创建指令。
更多技术交流群:251129682
|
|