基于ESP32的位置实时显示

[复制链接]
4147|0
 楼主| gaochy1126 发表于 2020-12-15 12:36 | 显示全部楼层 |阅读模式
本帖最后由 gaochy1126 于 2020-12-15 12:36 编辑

#申请原创#   @21小跑堂
之前玩航模的时候,飞机飞丢了。设备信号传输不好,飞丢位置找不到了,造成了一定的经济损失,所以想做一个位置跟踪和展示的设备。
这是做的一个简单的框架图。因为设备需要锂电池供电,所以功耗需要降低。
同时数据还需要同步到Web网页数据上,所以WebSocket 是最好的选择。
实现的模式是终端设备通过网络将位置数据传输到服务上,然后保存相关数据,并将数据推送到Web网页上显示。



先来个视频看看效果怎么样。这个是开着车跑了一圈,检测效果怎么样。基本上能够实时传输地址,说明通信的速度和显示的效果都可以的。
https://player.youku.com/embed/XNTAwODMxNTI1Mg==


看到很多的人都在玩ESP32。而且是arduino的开发环境,开发比较简单,而且性能和价格都比较便宜一些。第三方的库文件比较多,不需要自己另外的编写库函数。所以选择了ESP32作为处理器了。ESP32超低功耗, 专为移动设备、可穿戴电子产品和物联网应用而设计,具有业内高水平的低功耗性能,包括精细分辨时钟门控、省电模式和动态电压调整等。ESP32 只需极少的外围器件,即可实现强大的处理性能、可靠的安全性能,和 Wi-Fi & 蓝牙功能。


看到网上有SIM868,sim868是一款体积很小的2G模块,包含GPS GSM GPRS功能。定位精度2.5米左右。这样就节省了使用GPS模块的花费,而且SIM868还可以有网络定位的功能,如果前期GPS信号不好的时候,可以使用网络定位作为信号的补充。

服务器的购买环节就不在这里赘述了。现在新用户的优惠比较多一些。几百块钱可以用一年多,算是自己研究着玩吧。
通信协议选择了MQTT。 (MQTT) 是轻量级基于代理的发布/订阅的消息传输协议,设计思想是开放、简单、轻量、易于实现。小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量。而且在paho-mqtt库中,有一种重要的函数–回调函数。这个在网页开发比较简单了。

在服务器上搭建一个MQTT的服务器,有两种选择一个EMQ X和 mosquitto。MQ X Broker 是基于高并发的 Erlang/OTP 语言平台开发,支持百万级连接和分布式集群架构,发布订阅模式的开源 MQTT 消息服务器。Eclipse Mosquitto是一个开源消息代理,实现了MQTT协议版本3.1和3.1.1。Mosquitto轻量,适用于低功耗单板计算机到完整服务器的所有设备。
经过综合评估,于是在服务器上搭建了一个EMQ 的服务器,因为保存数据到SQL数据库,需要企业级别的才行(价格比较高)。所以选择了另外的方式进行保存:在MQTT服务器上搭建了Web_Hook的方式进行保存,节省了一笔开销。EMQ的安装建议大家百度一下,网上有很多的教程,建议开启账号和密码的验证功能,这样防止数据的泄漏。

页面展示使用百度地图。百度地图的开发比较成熟了,而且API函数非常的多,可以直接调用,性能也不错。
先从显示网页的开发介绍起来。

百度地图的显示建议搜索百度API代码非常的多。推荐地址http://lbsyun.baidu.com/index.php?title=jspopularGL 。查看demo的地址http://lbsyun.baidu.com/jsdemo.htm#a1_2
需要注意的是:GPS的经纬度转换到百度地图,需要多次转换才行。
目前国内主要有以下三种坐标系:

WGS84:为一种大地坐标系,也是目前广泛使用的GPS全球卫星定位系统使用的坐标系。

GCJ02:又称火星坐标系,是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。

BD09:为百度坐标系,在GCJ02坐标系基础上再次加密。其中bd09ll表示百度经纬度坐标,bd09mc表示百度墨卡托米制坐标。

附上一个JavaScript坐标系转换的代码。
  1.         var GPS = {
  2.             PI: 3.14159265358979324,
  3.             x_pi: 3.14159265358979324 * 3000.0 / 180.0,
  4.             delta: function (lat, lon)
  5.             {
  6.                 var a = 6378245.0; //  a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
  7.                 var ee = 0.00669342162296594323; //  ee: 椭球的偏心率。
  8.                 var dLat = this.transformLat(lon - 105.0, lat - 35.0);
  9.                 var dLon = this.transformLon(lon - 105.0, lat - 35.0);
  10.                 var radLat = lat / 180.0 * this.PI;
  11.                 var magic = Math.sin(radLat);
  12.                 magic = 1 - ee * magic * magic;
  13.                 var sqrtMagic = Math.sqrt(magic);
  14.                 dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * this.PI);
  15.                 dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * this.PI);
  16.                 return { 'lat': dLat, 'lon': dLon };
  17.             },

  18.             //WGS-84 to GCJ-02
  19.             gcj_encrypt: function (wgsLat, wgsLon)
  20.             {
  21.                 if (this.outOfChina(wgsLat, wgsLon))
  22.                     return { 'lat': wgsLat, 'lon': wgsLon };

  23.                 var d = this.delta(wgsLat, wgsLon);
  24.                 return { 'lat': wgsLat + d.lat, 'lon': wgsLon + d.lon };
  25.             },
  26.             //GCJ-02 to WGS-84
  27.             gcj_decrypt: function (gcjLat, gcjLon)
  28.             {
  29.                 if (this.outOfChina(gcjLat, gcjLon))
  30.                     return { 'lat': gcjLat, 'lon': gcjLon };

  31.                 var d = this.delta(gcjLat, gcjLon);
  32.                 return { 'lat': gcjLat - d.lat, 'lon': gcjLon - d.lon };
  33.             },
  34.             //GCJ-02 to WGS-84 exactly
  35.             gcj_decrypt_exact: function (gcjLat, gcjLon)
  36.             {
  37.                 var initDelta = 0.01;
  38.                 var threshold = 0.000000001;
  39.                 var dLat = initDelta, dLon = initDelta;
  40.                 var mLat = gcjLat - dLat, mLon = gcjLon - dLon;
  41.                 var pLat = gcjLat + dLat, pLon = gcjLon + dLon;
  42.                 var wgsLat, wgsLon, i = 0;
  43.                 while (1)
  44.                 {
  45.                     wgsLat = (mLat + pLat) / 2;
  46.                     wgsLon = (mLon + pLon) / 2;
  47.                     var tmp = this.gcj_encrypt(wgsLat, wgsLon)
  48.                     dLat = tmp.lat - gcjLat;
  49.                     dLon = tmp.lon - gcjLon;
  50.                     if ((Math.abs(dLat) < threshold) && (Math.abs(dLon) < threshold))
  51.                         break;

  52.                     if (dLat > 0) pLat = wgsLat; else mLat = wgsLat;
  53.                     if (dLon > 0) pLon = wgsLon; else mLon = wgsLon;

  54.                     if (++i > 10000) break;
  55.                 }
  56.                 //console.log(i);
  57.                 return { 'lat': wgsLat, 'lon': wgsLon };
  58.             },
  59.             //GCJ-02 to BD-09
  60.             bd_encrypt: function (gcjLat, gcjLon)
  61.             {
  62.                 var x = gcjLon, y = gcjLat;
  63.                 var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * this.x_pi);
  64.                 var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * this.x_pi);
  65.                 bdLon = z * Math.cos(theta) + 0.0065;
  66.                 bdLat = z * Math.sin(theta) + 0.006;
  67.                 return { 'lat': bdLat, 'lon': bdLon };
  68.             },
  69.             //BD-09 to GCJ-02
  70.             bd_decrypt: function (bdLat, bdLon)
  71.             {
  72.                 var x = bdLon - 0.0065, y = bdLat - 0.006;
  73.                 var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * this.x_pi);
  74.                 var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * this.x_pi);
  75.                 var gcjLon = z * Math.cos(theta);
  76.                 var gcjLat = z * Math.sin(theta);
  77.                 return { 'lat': gcjLat, 'lon': gcjLon };
  78.             },
  79.             //WGS-84 to Web mercator
  80.             //mercatorLat -> y mercatorLon -> x
  81.             mercator_encrypt: function (wgsLat, wgsLon)
  82.             {
  83.                 var x = wgsLon * 20037508.34 / 180.;
  84.                 var y = Math.log(Math.tan((90. + wgsLat) * this.PI / 360.)) / (this.PI / 180.);
  85.                 y = y * 20037508.34 / 180.;
  86.                 return { 'lat': y, 'lon': x };
  87.             },
  88.             // Web mercator to WGS-84
  89.             // mercatorLat -> y mercatorLon -> x
  90.             mercator_decrypt: function (mercatorLat, mercatorLon)
  91.             {
  92.                 var x = mercatorLon / 20037508.34 * 180.;
  93.                 var y = mercatorLat / 20037508.34 * 180.;
  94.                 y = 180 / this.PI * (2 * Math.atan(Math.exp(y * this.PI / 180.)) - this.PI / 2);
  95.                 return { 'lat': y, 'lon': x };
  96.             },
  97.             // two point's distance
  98.             distance: function (latA, lonA, latB, lonB)
  99.             {
  100.                 var earthR = 6371000.;
  101.                 var x = Math.cos(latA * this.PI / 180.) * Math.cos(latB * this.PI / 180.) * Math.cos((lonA - lonB) * this.PI / 180);
  102.                 var y = Math.sin(latA * this.PI / 180.) * Math.sin(latB * this.PI / 180.);
  103.                 var s = x + y;
  104.                 if (s > 1) s = 1;
  105.                 if (s < -1) s = -1;
  106.                 var alpha = Math.acos(s);
  107.                 var distance = alpha * earthR;
  108.                 return distance;
  109.             },
  110.             outOfChina: function (lat, lon)
  111.             {
  112.                 if (lon < 72.004 || lon > 137.8347)
  113.                     return true;
  114.                 if (lat < 0.8293 || lat > 55.8271)
  115.                     return true;
  116.                 return false;
  117.             },
  118.             transformLat: function (x, y)
  119.             {
  120.                 var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
  121.                 ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
  122.                 ret += (20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin(y / 3.0 * this.PI)) * 2.0 / 3.0;
  123.                 ret += (160.0 * Math.sin(y / 12.0 * this.PI) + 320 * Math.sin(y * this.PI / 30.0)) * 2.0 / 3.0;
  124.                 return ret;
  125.             },
  126.             transformLon: function (x, y)
  127.             {
  128.                 var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
  129.                 ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
  130.                 ret += (20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin(x / 3.0 * this.PI)) * 2.0 / 3.0;
  131.                 ret += (150.0 * Math.sin(x / 12.0 * this.PI) + 300.0 * Math.sin(x / 30.0 * this.PI)) * 2.0 / 3.0;
  132.                 return ret;
  133.             }
  134.         };

加载百度地图的关键代码 <script type="text/javascript" src="//api.map.baidu.com/api?v=2.0&ak=您的密钥"></script>其他的代码都是配置代码了。
然后再JavaScript编写MQTT的协议就行了。
下面开始做MQTT初始化;XXXX为服务器地址,需要配置服务器为Wss,即需要证书。
  1. client = new Paho.MQTT.Client("XXXXX", Number(8084), GenNonDuplicateID());
连接MQTT,并订阅 Pos的位置。
//初始化客户端选项 conn_opts
client.connect({
              onSuccess: onConnect
}
);
//连接服务器并注册连接成功处理事件
function onConnect() {
    console.log("onConnected");
    client.subscribe("pos", {
        qos: 0
    }
    );
}
client.onConnectionLost = onConnectionLost;
client.onMessageArrived = onMessageArrived;

这里是添加标志的代码
  1. var myIcon = new BMap.Icon("__INDEX__/img/car.gif", new BMap.Size(20, 40));
  2.         var point = new BMap.Point(lon, lat);
  3.         var marker = new BMap.Marker(point, {
  4.                     icon: myIcon
  5.                 });



未完待续。。。。。



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:这个社会混好的两种人:一是有权有势,二是没脸没皮的。

1205

主题

11937

帖子

26

粉丝
快速回复 在线客服 返回列表 返回顶部