开源OA办公平台搭建教程:O2OA+Arduino实现物联网应用(三)
本章我们介绍对设备进行初始化配置。在系统运行前,我们需要连接到wifi,连接到O2OA服务器,并以某个用户的身份登录到服务器,如果这些信息是在程序里写死的,那是很不合适的,所以我们必须有方法允许用户对设备的连接和登录信息进行初始化设置并随时可以更改。
我们通过启动一个AP接入点,并启动一个web服务,通过任何其他浏览器来对我们的设备进行配置。
创建项目文件
在VS Code中创建一个工作区,在工作区中创建一个文件夹:“o2iot”。然后再此文夹下创建文件:“o2iot.ino”文件,并输入以下代码:
//引用Arduino核心库 void setup() { //设备上电后初始化执行 } void loop() { //设备运行期间循环执行 }
此时,工作区右下角出现几个选项
点击“Select Board Types”,选择合适的开发板类型,此处我选择“LOLIN(WEMOS) D1 R2 & mini (esp8266)”(可以根据使用的不同开发板选择)
点击“Select Serial Port”选择开发板连接的COM口,可以在我们需要上载程序到开发板时再选择。
在.ino文件窗口的右上角,我们发现有两个按钮:分别用于上载程序到开发板和验证程序:
创建O2IOTServer类
我们要使ESP8266启用AP模式,并响应web服务,用于设置要连接的wifi,O2OA服务器的地址,登录用户等信息,完成设备的初始化,所以我们先创建O2IOTServer类,完成此功能。
在o2iot目录下创建文件:O2IOTServer.h,代码如下:
//引入ESP8266的webserver,用于创建web服务器 //引入ESP8266的WiFi,用于创建AP接入点 //Arduino核心库,初始化信息需要存入EEPROM存储器中 //接入点名称 //接入点默认密码 class O2IOTServer { public: bool begin(); //开始web服务 void listening(); //监听http请求 //初始化配置信息变量 String o2host=""; String o2port=""; String o2user=""; String o2pass=""; char *ssid_name; char *ssid_pass; //从EEPROM中获取配置信息 void getConfig(); //解析配置信息 void parseConfig(); private: static ESP8266WebServer *_server; //webserver对象 static ESP8266WiFiScanClass *_wifiScan; //wifi扫描对象,用于扫描可用的wifi热点 static void _bindService(); //将http请求的不同路径,绑定到不同的方法 static void _setConfig(); //将获取到的配置信息,写入EEPROM存储器 static void _handleRoot(); //当http访问路径为“/”时响应 static void _handleSet(); //当http访问路径为“/config”时响应 static char _ssidConfig[128]; //配置信息存储128字节 }; extern O2IOTServer O2_IOTServer; /* O2IOTSERVER_H_ */
然后我们需要创建.cpp来实现头文件中定义的方法,所以在o2iot目录下创建文件:O2IOTServer.cpp,代码如下:
//初始化WebServer端口为80 ESP8266WebServer* O2IOTServer::_server = new ESP8266WebServer(80); ESP8266WiFiScanClass* O2IOTServer::_wifiScan; char O2IOTServer::_ssidConfig[128]; bool O2IOTServer::begin() { EEPROM.begin(512); //ESP8266使用EEPROM需要首先调用EEPROM.begin(size)方法 if (WiFi.softAP(AP_SSID, AP_PASSWORD)) { //通过默认的AP名称和密码启动接入点. _bindService(); //将http请求的不同路径,绑定到不同的方法 _server->begin(); //启动Web服务器 return true; }else { return false; } } void O2IOTServer::listening() { _server->handleClient(); //监听http请求 } void O2IOTServer::_bindService(){ _server->on("/", _handleRoot); //将http请求路径"/",通过_handleRoot方法响应 _server->on("/config", _handleSet); //将http请求路径"/config",通过_handleSet方法响应 } //当http请求路径"/"时,显示的html信息 //创建了一个超链接,“set you config”,点击时访问/config void O2IOTServer::_handleRoot(){ String content = "<html><meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />"; content += "<meta content='yes' name='apple-mobile-web-app-capable' />"; content += "<meta content='black' name='apple-mobile-web-app-status-bar-style' />"; content += "<meta content='telephone=no' name='format-detection' />"; content += "<body><H2>hello, you successfully connected to O2IOT</H2><br>"; content += "You can access this page <a href=\"/config\">set you config</a></body></html>"; _server->send(200, "text/html", content); } //当http请求路径"/config"时,显示的html信息 void O2IOTServer::_handleSet(){ char ept = '\0'; //如果请求信息包含配置信息参数,说明是提交配置信息 if (_server->hasArg("ssid") && _server->hasArg("pass") && _server->hasArg("o2user") && _server->hasArg("o2pass") && _server->hasArg("o2host") && _server->hasArg("o2port")) { String config = _server->arg("ssid") + "|" + _server->arg("pass")+ "|" + _server->arg("o2user")+ "|" + _server->arg("o2pass")+ "|" + _server->arg("o2host")+ "|" + _server->arg("o2port"); int len = config.length(); //将配置信息使用"|"符号分隔后,存入_ssidConfig中 for (int i = 0; i < 128; i++) { if (len > i) { _ssidConfig[i] = config.charAt(i); }else { _ssidConfig[i] = ept; } } //返回html,提示用户配置信息已经收到 String rStr = "<html><meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />"; rStr += "<meta content='yes' name='apple-mobile-web-app-capable' />"; rStr += "<meta content='black' name='apple-mobile-web-app-status-bar-style' />"; rStr += "<meta content='telephone=no' name='format-detection' />"; rStr += "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />"; rStr += "<body><div style='text-align:center; font-size: 16px; margin-top:100px'>您已经更改了设置,正在重新连接... </div></body>"; _server->send(200, "text/html", rStr);; //将配置信息写入到EEPROM中 _setConfig(); return; } //如果请求信息没有包含配置信息,则让用户填写配置信息。 //填写完成点击submit按钮,将信息提交到"/config"。 //通过_wifiScan对象,扫描所有可用的wifi热点 int n = _wifiScan->scanNetworks(); //响应的html内容 String content = "<html><head><meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />"; content += "<meta content='yes' name='apple-mobile-web-app-capable' />"; content += "<meta content='black' name='apple-mobile-web-app-status-bar-style' />"; content += "<meta content='telephone=no' name='format-detection' /></head>"; content += "<body><form action='/config' method='POST' onsubmit='return check();'>input your wifi ssid and password<br>"; content += "select ssid:<!--<input type='text' name='ssid1' placeholder='ssid name'>--><br>"; //列出所有可用热点,让用户选择 for (int i = 0; i < n; i++) { uint8_t x = i; String n = _wifiScan->SSID(x); content += "ssid:<input type='radio' name='ssid' value='"+ n +"'>"+ n +"<br>"; } content += "input wifi password:<input type='password' name='pass' placeholder='password'><br>"; content += "<hr>"; //从EEPROM获取已有的配置信息 //回填到下面需要用户配置的O2服务器域名、端口、登录用户名和密码 O2_IOTServer.getConfig(); O2_IOTServer.parseConfig(); String port = O2_IOTServer.o2port; if (port.equalsIgnoreCase("")) port="20030"; content += "input o2Server center host:<input type='text' name='o2host' placeholder='input o2 host' value='"+O2_IOTServer.o2host+"'><br>"; content += "input o2Server center port:<input type='text' name='o2port' placeholder='input o2 port' value='"+port+"'><br>"; content += "input o2Server user:<input type='text' name='o2user' placeholder='input o2 user' value='"+O2_IOTServer.o2user+"'><br>"; content += "input o2Server user password:<input type='password' name='o2pass' value='"+O2_IOTServer.o2pass+"'><br>"; content += "<input type='submit' name='SUBMIT' value='Submit'></form><br>"; content += "<script>function check(){if (!document.all.pass.value || !document.all.o2host.value || !document.all.o2port.value || !document.all.o2user.value || !document.all.o2pass.value){alert('please input wifi password and o2 host,port,user,password '); return false; }; return true; }</script>"; content += "</body></html>"; _server->send(200, "text/html", content); } void O2IOTServer::_setConfig() { // for (int i = 0; i < 128; i++) { EEPROM.write(i, _ssidConfig[i]); delay(5); } EEPROM.commit(); //ESP8266需要使用EEPROM.commit()方法保存数据 } void O2IOTServer::getConfig() { for (int i = 0; i < 128; i++) { _ssidConfig[i] = EEPROM.read(i); } } void O2IOTServer::parseConfig() { if (strchr(_ssidConfig, '|')!=NULL){ if (_ssidConfig[0] != NULL) { O2_IOTServer.ssid_name = strtok(_ssidConfig, "|"); O2_IOTServer.ssid_pass = strtok(NULL, "|"); O2_IOTServer.o2user = strtok(NULL, "|"); O2_IOTServer.o2pass = strtok(NULL, "|"); O2_IOTServer.o2host = strtok(NULL, "|"); O2_IOTServer.o2port = strtok(NULL, "|"); } } } O2IOTServer O2_IOTServer;
现在我们完成了O2IOTServer类,实现了启动AP接入点,监听Web服务,并可以设置设备初始化信息了。
在主程序中调用
我们回到o2iot.ino文件,更新一下代码,调用O2IOTServer类。
//引用Arduino核心库 //引用O2IOTServer类 void setup() { O2_IOTServer.begin(); //启动AP接入点,并启动Web服务 } void loop() { O2_IOTServer.listening(); //监听http请求 }
整个程序的逻辑过程基本上是如下图的样子:
验证和上载程序
然后我们验证一下程序,点击右上角的验证按钮,或使用快捷键“Ctrl+Alt+R”,等待片刻后,在输出端,应该可以看到以下信息:
然后我们通过usb连接开发板,正确选择开发板类型和COM端口后,可以上载我们的程序,点击右上角的上载按钮,或使用快捷键“Ctrl+Alt_U”,等待片刻后,在输出端,应该可以看到以下信息:
说明我们的程序上载成功了。
看看效果
此时,我们开发板上已经开始运行我们的程序了,我们可以通过其他具有wifi的终端浏览器,连接到o2iot这个热点,访问并设置我们的设备。
这里我通过手机连接到o2iot
然后打开浏览器,输入“http://192.168.4.1”(esp8266默认IP地址为192.168.4.1)
点击“set your config”
此处选择要连接的热点,输入相关的信息后,点“submit”
OK,我们设置的信息已经存储下来了,再次访问“http://192.168.4.1/config”,就可以看到我们上一次存储的配置信息。
下一章将介绍将设备作为客户端连接到wifi热点,并登录到O2OA服务器。