玩转OneNET物联网平台之MQTT服务⑦ —— 远程控制LED(数量无限制)+ Android App控制 优化第一版
- 2019 年 10 月 31 日
- 筆記
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力。希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石。。。
QQ技术互动交流群:ESP8266&32 物联网开发 群号622368884,不喜勿喷
一、你如果想学基于Arduino的ESP8266开发技术
一、基础篇
二、网络篇
- ESP8266开发之旅 网络篇① 认识一下Arduino Core For ESP8266
- ESP8266开发之旅 网络篇② ESP8266 工作模式与ESP8266WiFi库
- ESP8266开发之旅 网络篇③ Soft-AP——ESP8266WiFiAP库的使用
- ESP8266开发之旅 网络篇④ Station——ESP8266WiFiSTA库的使用
- ESP8266开发之旅 网络篇⑤ Scan WiFi——ESP8266WiFiScan库的使用
- ESP8266开发之旅 网络篇⑥ ESP8266WiFiGeneric——基础库
- ESP8266开发之旅 网络篇⑦ TCP Server & TCP Client
- ESP8266开发之旅 网络篇⑧ SmartConfig——一键配网
- ESP8266开发之旅 网络篇⑨ HttpClient——ESP8266HTTPClient库的使用
- ESP8266开发之旅 网络篇⑩ UDP服务
- ESP8266开发之旅 网络篇⑪ WebServer——ESP8266WebServer库的使用
- ESP8266开发之旅 网络篇⑫ 域名服务——ESP8266mDNS库
- ESP8266开发之旅 网络篇⑬ SPIFFS——ESP8266 Flash文件系统
- ESP8266开发之旅 网络篇⑭ web配网
- ESP8266开发之旅 网络篇⑮ 真正的域名服务——DNSServer
- ESP8266开发之旅 网络篇⑯ 无线更新——OTA固件更新
三、应用篇
四、高级篇
1.前言
在前面的博文 玩转OneNET物联网平台之MQTT服务④ —— 远程控制LED(数量无限制)+ Android App控制 中,博主只是大体上讲述了整个小项目的构造。但是,如果作为一个产品来开发的话,还是存在不少问题。这里罗列几个我认为比较重要的问题点:
- 问题1:App作为一个特殊的设备,理论上也应该支持自注册功能,不应该由开发者或者用户额外去调用API调试工具创建设备,起码得简化这个流程;
- 问题2:App没有处理设备从在线状态切换成离线状态的过程,需要实时更新设备状态;
接下来,博主就会针对这两个重要的问题点进行解决思路以及解决步骤的讲解,请读者边思考边实验。
2.解决问题点1
-
App作为一个特殊的设备,理论上也应该支持自注册功能,不应该由开发者或者用户额外去调用API调试工具创建设备,起码得简化这个流程
2.1 解决思路
- 我们目的无非是为了得到一个真实存在的DeviceID,既然OneNet 平台给我们提供了 新增设备 的API,那么我们可以通过它来创建设备并且获取设备ID。
-
因为创建设备需要设备唯一性标识,而android手机上唯一性东西非常多,我们这里考虑用设备序列号
Android系统2.3版本以上可以通过下面的方法得到Serial Number,且非手机设备也可以通过该接口获取。 String serial= android.os.Build.SERIAL;
2.2 解决步骤
- 修改app逻辑,加上注册方法
/** * 新增OneNet设备 */ public class RegisterOneNetDeviceEntity extends BaseResponseEntity { public dataModel data; public static class dataModel{ public String device_id; } @Override protected String createArgs(Object... params) { OneNetDeviceModel model = (OneNetDeviceModel) params[0]; JSONObject object = new JSONObject(); try { object.put("title",model.getTitle()); object.put("auth_info",model.getAuth_info()); } catch (JSONException e) { e.printStackTrace(); } return object.toString(); } @Override protected String makeUrl() { return API_POST_REGISTER_DEVICE; } public String request(OneNetDeviceModel model) { method = HttpUtilCore.Method.Post; String json = requestJson(model); //内部消化 return handleResponse(json, new OnResponseListener<RegisterOneNetDeviceEntity>() { @Override public void onSuccess(String json, RegisterOneNetDeviceEntity response) { data = response.data; } @Override public void onFailed(String reason) { Logger.d(reason); } @Override public void onTimeout() { Logger.d("请求超时"); } }); } }
- 用户点击注册,加上设备序列号
@Override public void onInitView(Bundle savedInstanceState) { PreferenceUtil preference = PreferenceUtil.getInstance(); if(!TextUtils.isEmpty(preference.getDeviceId())){ this.onConfigConfirm(); return; } getDefaultNavigation().setTitle("配置智能灯"); getDefaultNavigation().getLeftButton().hide(); etDevice.setText("Android_" + android.os.Build.SERIAL); etProduct.setText(preference.getProductId()); etApikey.setText(preference.getApiKey()); }
- 新增成功之后,存储服务器返回的device_id
@Override public void register(Context context, final OneNetConfigDALEx config, final ICallBack<String> callBack) { if(task != null && task.getStatus()== AsyncTask.Status.RUNNING){ task.cancel(true); } task = new SimpleTask() { OneNetDeviceModel device; RegisterOneNetDeviceEntity entity; @Override protected void onPreExecute() { PreferenceUtil preference = PreferenceUtil.getInstance(); preference.writePreferences(PreferenceUtil.ApiKey,config.getApikey()); preference.writePreferences(PreferenceUtil.ProductId,config.getProductId()); entity = new RegisterOneNetDeviceEntity(); device = new OneNetDeviceModel(); device.setTitle(config.getDeviceId()); device.setAuth_info(config.getDeviceId()); } @Override protected Object doInBackground(String... strings) { return entity.request(device); } @Override protected void onPostExecute(Object o) { String result = (String) o; PreferenceUtil preference = PreferenceUtil.getInstance(); if("200".equals(result)){//保存设备id preference.writePreferences(PreferenceUtil.DeviceId,entity.data.device_id); callBack.onSuccess(""); }else { callBack.onFaild(result); } } }; task.startTask();
- 这样就完成了手机自注册设备功能
这种方案有个好处就是:
- 非常适合多个成员同时控制一组智能灯,并且不会产生掉线的情况。
但是,请读者注意:
-
如果你先注册了一次,然后卸载app,再重新注册,这个时候会失败的(原因请读者自行分析)。这个时候请到后台手动删除Android_xxxx的设备。
3.解决问题点2
- 问题点:App没有处理设备从在线状态切换成离线状态的过程,需要实时更新设备状态
3.1 解决方案
目前大概有几种方案:
- 方案a:手机端不断轮询批量获取设备列表接口,可以设置较大的时间间隔,这个方案准确性高,但是耗费流量。目前这个方案相对靠谱。
- 方案b:esp8266端发布设备状态topic和信息,手机端订阅该topic,通过mqtt来更新状态;
- 方案c:利用mqtt的遗嘱消息,但是这个博哥验证了OneNet的,感觉不好用。
- 方案d:如果mqtt的遗嘱消息比较快速实时,可以考虑方案b+c的组合,个人觉得这个是比较理想的方案。(不过,目前我还没有很好实现)
- 方案e:本项目不追求实时性,我们可以手动下拉刷新或者在activity的onshow自动刷一遍,基本上满足要求,采取该方案。
4.总结
- 问题1:App作为一个特殊的设备,理论上也应该支持自注册功能,不应该由开发者或者用户额外去调用API调试工具创建设备,起码得简化这个流程;可以解决。
- 问题2:App没有处理设备从在线状态切换成离线状态的过程,需要实时更新设备状态;赞未完美解决,只能兜底方案。
- 但是,我们离产品化越来越近了。