UWB中TOF测距法的公式推导

UWB中TOF测距法的公式推导

UWB常用测距方法有两种:飞行时间测距法(TOF)和到达时间差法(TDOA)。这里说一下TOF。

TOF

飞行时间法(Time of Flight,TOF)是一种双向测距技术,它通过测量UWB信号在基站与标签之间往返的飞行时间来计算距离。根据数学关系,一点到已知点的距离为常数,那么这点一定在以已知点为圆心,以该常数为半径的圆上。有两个已知点,就有两个交点。以三个已知点和距离作三个圆,他们交于同一个点,该点就是标签的位置。

TOF定位方式需要基站和标签往返通信,因此就造成了TOF功耗大大提高,续航时间相对较短.

TOF又分为两种:单边双向测距和双边双向测距。

单边双向测距

单边双向测距(Single-sided Two-way Ranging: SS-TWR)是对单个往返消息时间上的简单测量,设备A主动发送数据到设备B,设备B返回数据响应设备A。如下图所示:

单边双向测距的流程是这样的:设备A(Device A)主动发送(TX)数据,同时记录发送时间戳,设备B(Device B)接收到之后记录接收时间戳;延时 $T_{reply}$ 之后,设备B发送数据,同时记录发送时间戳,设备A接收数据,同时记录接收时间戳。

所以可以拿到两个时间差数据,设备A的时间差 $T_{round}$ 和设备B的时间差 $T_{reply}$ ,最终得到无线信号的飞行时间 $T_{prop}$ 如下:

$ T_{prop} = \frac{1}{2}(T_{round}-T_{reply}) $

两个差值时间都是基于本地的时钟计算得到的,本地时钟误差可以抵消,但是不同设备之间会存在微小的时钟偏移,假设设备A和B的时钟偏移分别为 $e_A$ 和 $e_B$ ,则飞行时间测量值为:

$ \hat{T}_{prop} = \frac{1}{2}[T_{round}(1+e_A)-T_{reply}(1+e_B)] $

于是测距误差如下:

$ Error = \hat{T}_{prop} - T_{prop} = \frac{1}{2}(T_{round}\cdot e_A-T_{reply}\cdot e_B) = \frac{1}{2}T_{reply}(e_A-e_B) + T_{prop}\cdot e_A $

因为 $ T_{reply} >> T_{prop} $ , 所以可以忽略后一项,得到

$ Error = \hat{T}_{prop} - T_{prop} \approx \frac{1}{2}T_{reply}(e_A-e_B) $

由此可以看出,随着 $T_{reply} $ 和时钟偏移的增加,会增加飞行时间的误差,从而使得测距不准确。因此单边双向测距(SS-TWR)并不常用,但对于特定的应用,如果对于精度要求不是很高,但是需要更短的测距时间可以采用。注意 $ T_{reply} $ 不仅仅是设备B接收到发送的时间,也包括装载数据和发送数据耗费的时间(UWB除了支持定位之外,也可以传输数据,标准可以装载128字节,扩展模式可以装载1024字节数据)。

双边双向测距

双边双向测距(Double-sided Two-way Ranging)是单边双向测距的一种扩展测距方法,记录了两个往返的时间戳,最后得到飞行时间,虽然增加了响应的时间,但会降低测距误差。

双边双向测距分为两次测距,设备A主动发起第一次测距消息,设备B响应,当设备A收到数据之后,再返回数据,最终可以得到如下四个时间差:$ T_{round1} $ 、$ T_{reply1} $ 、$ T_{round2} $ 、$ T_{reply2} $ ,如下图所示:

双边双向测距飞行时间计算方法:

由单边双向测距方法可得

$ T_{prop} = \frac{1}{2}(T_{round1}-T_{reply1}) $

$ T_{prop} = \frac{1}{2}(T_{round2}-T_{reply2}) $

所以

$ \begin{flalign}T_{round1} \times T_{round2} = (2T_{prop}+T_{reply1})(2T_{prop}+T_{reply2}) = 4T_{prop}^2+2T_{prop}(T_{reply1}+T_{reply2})+T_{reply1}T_{reply2} \end{flalign}$

$ \begin{flalign} T_{round1} \times T_{round2} - T_{reply1}T_{reply2} = 4T_{prop}^2+2T_{prop}(T_{reply1}+T_{reply2}) \\ = T_{prop}(4T_{prop}+2T_{reply1}+2T_{reply2}) \\ = T_{prop}(T_{round1} + T_{round2} + T_{reply1} + T_{reply2}) \end{flalign}$

于是可以得到如下计算 $ T_{prop} $ 的公式:

$ T_{prop} = \frac{T_{round1} \times T_{round2} - T_{reply1} \times T_{reply2}}{T_{round1} + T_{round2} + T_{reply1} + T_{reply2}} $

以上测距的机制是非对称的测距方法,因为他们对于响应时间不要求是相同的。下面分析双边双向测距飞行时间的误差:

$ \begin{flalign} \hat{T}_{prop} = \frac{T_{round1}(1+e_A) \times T_{round2}(1+e_B) - T_{reply1}(1+e_B) \times T_{reply2}(1+e_A)}{T_{round1}(1+e_A) + T_{round2}(1+e_B) + T_{reply1}(1+e_B) + T_{reply2}(1+e_A)} \\ = \frac{(4T_{prop}^2+2T_{prop}(T_{reply1}+T_{reply2}))(1+e_A)(1+e_B)} {4T_{prop}+2(T_{reply1}+T_{reply2})+(2T_{prop}+T_{reply1}+T_{reply2})(e_A+e_B)}\\ =\frac{2(1+e_A)(1+e_B)}{(1+e_A)+(1+e_B)}T_{prop} \end{flalign}$

于是

$ T_{prop} = \frac{(1+e_A)+(1+e_B)}{2(1+e_A)(1+e_B)}\hat{T}_{prop} $

$ \begin{flalign} Error = \hat{T}_{prop} - T_{prop} = \left(1-\frac{(1+e_A)+(1+e_B)}{2(1+e_A)(1+e_B)}\right)\hat{T}_{prop} \\ = \frac{e_A+e_B+2e_A e_B}{2(1+e_A)(1+e_B)}\hat{T}_{prop} \end{flalign} $

因为 $ e_A <<1 $,$ e_B<<1$,略去高次项,可得

$ Error \approx \frac{e_A+e_B}{2}\hat{T}_{prop} $

由此可以看出,误差仅与钟漂和飞行时间有关。

假设一个使用场景:使用20ppm的晶体,UWB的工作距离范围为300m,则无线信号空中飞行时间大概为$1 \mu s$,误差为$ 20 \times 10^{-6} \times 1 \times 10^{-6} = 20 \times 10^{-12} = 20ps $时钟误差是在ps级别的,换算为距离之后仅为6mm。

注意:响应时间是不需要相等的,也就是 $ T_{reply1} $ 不一定要等于 $ T_{reply2} $ ,这样对于MCU系统的处理带来了很多便利。

若双边双向测距方法响应时间对称,也就是 $ T_{reply1} $ 和 $ T_{reply2} $ 相等,飞行时间计算方法如下:

$ T_{prop} = \frac{1}{4}(T_{round1}-T_{reply1}+T_{round2}-T_{reply2}) $

这种方法比较简单,只是需要一些时间戳做加减法,但其难点在于,怎么保证 $T_{reply1} $ 和 $ T_{reply2} $ 是相等的。

此种方法的误差分析如下:

$ \begin{flalign} \hat{T}_{prop} = \frac{1}{4}\left[T_{round1}(1+e_A)-T_{reply1}(1+e_B)+T_{round2}(1+e_B)-T_{reply2}(1+e_A)\right] \end{flalign}$

$ \begin{flalign} Error = \hat{T}_{prop} - T_{prop} = \frac{1}{4}\left[(T_{round1}-T_{reply2})e_A +(T_{round2}-T_{reply1})e_B\right] \\ =\frac{1}{4}\left[2(e_A+e_B)T_{prop} +(e_A-e_B)(T_{reply1}-T_{reply2})\right] \end{flalign} $

因为 $ T_{reply1}-T_{reply2} >> T_{prop} $ ,可忽略$ T_{prop} $项,从而得到

$ Error \approx \frac{1}{4}(e_A-e_B)(T_{reply1}-T_{reply2})$

可见此误差与响应时间差成正比。

参考链接


智能硬件Nvidia Jetson Nano B01

产品参数

GPU 128-core Maxwell
CPU Quad-core ARM A57 @ 1.43 GHz
内存 4 GB 64-bit LPDDR4 25.6 GB/s
存储 micro SD 卡 
视频编码 4K @ 30   |   4x 1080p @ 30   |   9x 720p @ 30
(H.264/H.265)
视频解码 4K @ 60   |   2x 4K @ 30   |   8x 1080p @ 30   |   18x 720p @ 30
(H.264/H.265)
摄像头 2x MIPI CSI-2 D-PHY lanes
联网 千兆以太网,M.2 Key E 接口外扩 (可外接: AC8265 双模网卡 )
显示 HDMI 和 DP 显示接口
USB 4x USB 3.0,USB 2.0 Micro-B
扩展接口 GPIO,I2C,I2S,SPI,UART
其他 260-pin 连接器

Jetson Nano系统安装

1、JetPack介绍

JetPack SDK包括最新的Linux驱动程序包(L4T),带有Linux操作系统和CUDA-X加速库,以及用于深度学习、计算机视觉、加速计算和多媒体的API。 它还包括用于主机和开发人员套件的示例、文档和开发人员工具,并支持更高级别的SDK,例如用于流式视频分析的DeepStream和用于机器人的Isaac。

2、JetPack 4.4

JetPack 目前最新版本是4.4,支持Vulkan 1.2、TensorRT 7.1.3 、cuDNN 8.0、CUDA 10.2 等。

3、下载和安装

  • 下载 Jetson Nano镜像,镜像中包含提供引导加载程序、Ubuntu18.04、必要的固件、NVIDIA驱动程序、示例文件系统等。
  • 使用 Etcher 或者 Raspberry Pi Imager 将镜像烧录到SD卡(建议至少32G)中。

设置VNC服务

1.执行更新

2.安装vino服务端

这个vino服务端我使用的镜像文件是安装好了的,但是古早版的镜像文件可能没有,所以可以执行下代码看看是否有安装。

3.开启VNC 服务

4.配置VNC服务

设置开机自启动

1.创建VNC自动启动文件

创建文件夹,然后创建一个自动启动文件

2.添加以下内容到vino-server.desktop文件中

这个时候,虽说是自动启动了,但是只有进入桌面后才自动启动服务,所以需要取消登录密码,启动就进入桌面。

参考链接


关于HTTP请求走私的小记

0x00 写在前面

之前一次线上赛,遇到一道Web题,涉及了HTTP请求走私。由于之前未学习过,从而我展开了HTTP请求走私的学习之旅。

0x01 HTTP请求走私是什么

HTTP请求走私是一种干扰网站处理从一个或多个用户接收的HTTP请求序列的方式的技术。使攻击者可以绕过安全控制,未经授权访问敏感数据并直接危害其他应用程序用户。

0x02 为什么会产生HTTP请求走私

请求走私漏洞成因

前端服务器(CDN)和后端服务器接收数据不同步,引起对客户端传入的数据理解不一致,从而导致漏洞的产生。

大多数HTTP请求走私漏洞的出现是因为HTTP规范提供了两种不同的方法来指定请求的结束位置:Content-Length标头和Transfer-Encoding标头。
同时使用两种不同的方法时,Content-Length无效。当使用多个服务器时,对客户端传入的数据理解不一致时,就会出现有些服务器认为Content-Length的长度有效,有些以Transfer-Encoding有效。而一般情况下,反向代理服务器与后端的源站服务器之间,会重用TCP链接。这样超出的长度就会拼接到下一次请求进行请求,从而导致HTTP请求走私漏洞。

RFC2616规范

如果接收的消息同时包含传输编码头字段(Transfer-Encoding)和内容长度头(Content-Length)字段,则必须忽略后者。

由于规范默许可以使用Transfer-EncodingContent-Length处理请求,因此很少有服务器拒绝此类请求。每当我们找到一种方法,将Transfer-Encoding隐藏在服务端的一个chain中时,它将会回退到使用Content-Length去发送请求。

走私攻击实现

当向代理服务器发送一个比较模糊的HTTP请求时,由于两者服务器的实现方式不同,代理服务器可能认为这是一个HTTP请求,然后将其转发给了后端的源站服务器,但源站服务器经过解析处理后,只认为其中的一部分为正常请求,剩下的那一部分,就算是走私的请求,当该部分对正常用户的请求造成了影响之后,就实现了HTTP走私攻击。

扩展:为什么会出现多次请求

这与最为广泛的HTTP 1.1的协议特性——Keep-Alive&Pipeline有关。

HTTP1.0之前的协议设计中,客户端每进行一次HTTP请求,需要同服务器建立一个TCP链接。

而现代的Web页面是由多种资源组成的,要获取一个网页的内容,不仅要请求HTML文档,还有JS、CSS、图片等各种资源,如果按照之前的协议设计,就会导致HTTP服务器的负载开销增大。于是在HTTP1.1中,增加了Keep-AlivePipeline这两个特性。

Keep-Alive:在HTTP请求中增加一个特殊的请求头Connection: Keep-Alive,告诉服务器,接收完这次HTTP请求后,不要关闭TCP链接,后面对相同目标服务器的HTTP请求,重用这一个TCP链接。这样只需要进行一次TCP握手的过程,可以减少服务器的开销,节约资源,还能加快访问速度。这个特性在HTTP1.1中默认开启的。

Pipeline(http管线化):http管线化是一项实现了多个http请求但不需要等待响应就能够写进同一个socket的技术,仅有http1.1规范支持http管线化。在这里,客户端可以像流水线一样发送自己的HTTP请求,而不需要等待服务器的响应,服务器那边接收到请求后,需要遵循先入先出机制,将请求和响应严格对应起来,再将响应发送给客户端。

现在,浏览器默认不启用Pipeline的,但是一般的服务器都提供了对Pipleline的支持。

继续阅读关于HTTP请求走私的小记

Transfer-Encoding 的作用

通过HTTP传送数据时,有些时候并不能事先确定body的长度,因此无法得到Content-Length的值, 就不能在header中指定Content-Length了,造成的最直接的影响就是:接收方无法通过Content-Length得到报文体的长度, 那怎么判断发送方发送完毕了呢?HTTP 1.1协议在header中引入了Transfer-Encoding,当其值为chunked时, 表明采用chunked编码方式来进行报文体的传输

HTTP 1.1中有两个实体头(Entity-Header)直接与编码相关,分别为Content-Encoding和Transfer-Encoding.
先说Content-Encoding, 该头表示实体已经采用了的编码方式.Content-Encoding是请求URL对应实体(Entity)本身的一部分.比如请求URL为http://host/image.png.gz时,可能会得到的Content-Encoding为gzip.Content-Encoding的值是不区分大小写的,目前HTTP1.1标准中已包括的有gzip/compress/deflate/identity等.
与Content-Encoding头对应,HTTP请求中包含了一个Accept-Encoding头,该头用来说明用户代理(User-Agent,一般也就是浏览器)能接受哪些类型的编码. 如果HTTP请求中不存在该头,服务器可以认为用户代理能接受任何编码类型.

接下来重点描述Transfer-Encoding, 该头表示为了达到安全传输或者数据压缩等目的而对实体进行的编码. Transfer-Encoding与Content-Encoding的不同之处在于:
1, Transfer-Encoding只是在传输过程中才有的,并非请求URL对应实体的本身特性.
2, Transfer-Encoding是一个"跳到跳"头,而Content-Encoding是"端到端"头.
该头的用途举例如,请求URL为http://host/abc.txt,服务器发送数据时认为该文件可用gzip方式压缩以节省带宽,接收端看到Transfer-Encoding为gzip首先进行解码然后才能得到请求实体.
此外多个编码可能同时对同一实体使用,所以Transfer-Encoding头中编码顺序相当重要,它代表了解码的顺序过程.同样,Transfer-Encoding的值也是不区分大小写的,目前HTTP1.1标准中已包括的有gzip/compress/deflate/identity/chunked等.
Transfer-Encoding中有一类特定编码:chunked编码.该编码将实体分块传送并逐块标明长度,直到长度为0块表示传输结束, 这在实体长度未知时特别有用(比如由数据库动态产生的数据). HTTP1.1标准规定,只要使用了Transfer-Encoding的地方就必须使用chunked编码,并且chunked必须为最后一层编码.任何HTTP 1.1应用都必须能处理chunked编码.
与Transfer-Encoding对应的请求头为TE,它主要表示请求发起者愿意接收的Transfer-Encoding类型. 如果TE为空或者不存在,则表示唯一能接受的类型为chunked.
其他与Transfer-Encoding相关的头还包括Trailer,它与chunked编码相关,就不细述了.

顾名思义,Content-Length表示传输的实体长度,以字节为单位(在请求方法为HEAD时表示会要发送的长度,但并不实际发送.).Content-Length受Transfer-Encoding影响很大,只要Transfer-Encoding不为identity,则实际传输长度由编码中的chunked决定,Content-Length即使存在也被忽略.

关于HTTP Message Body的长度
在HTTP中有消息体(Message body)和实体(Entity body)之分,简单说来在没有Transfer-Encoding作用时,消息体就是实体,而应用了Transfer-Encoding后,消息体就是编码后的实体,如下:

具体详细的 RFC 7230 说明如下:

参考链接


Transfer-Encoding 的作用

RS485 软件流控 及 数据完整性确保

首先 串口 的流控大家应该都有所了解,通常是硬件 CTS/RTS 或软件 XON/XOFF 这两种流控方式,然而因为 RS485 是总线形式,所以传统的方法都不再适用。

有人会觉得奇怪,貌似从来没有考虑过 RS485 流控的问题,没错,传统 RS485 都是一收一发,用不着考虑流控,然而这种一收一发的效率比较低,譬如在 IoT 火热的今天,如果用 RS485 来传输网络数据,那么传统的做法就很低效了。

然后,针对数据完整性确保的问题,很多同行都没有留意到一个细节问题,他们通常判断是否收到回复 OK 的数据包,如果没收到数据包就超时重发一次。 这种做法大多情况都没有问题,但是某些场景,譬如发送一个命令让滑轨左移 10mm, 滑轨成功接收命令并返回 OK, 然而主机因为干扰等各种问题没有收到滑轨的回复,那么重发命令就会导致滑轨错误左移 20mm. 当然你可以说目前用到的设备都是绝对位置控制,不会有影响,但万一哪天新做一个设备,到那时再改协议,难道就不考虑兼容自己以往的产品了吗?

当然还是有很多朋友有注意到这个问题,本文使用的解决方法原理上跟这些朋友也是相同的。

我接下来提出的方案最大的亮点是共用同一套机制,同时解决了流控、数据完整性确保、大数据分包等功能,而且比较高效和简单。

同样,最底层的协议我们依然使用 CDBUS, 因为它比较简单,又支持硬件增强(可以主动避让冲突,实现多主机、对等通讯、主动上报数据等功能),能最大程度体现出本文方法的性能优势。

你可能没有听过 CDBUS 这个名字,但你可能曾经或正在使用相似的协议,它的组成包含 3 个部分: - 3 个字节的头:「源地址,目标地址,用户数据长度」 - 0~255 字节的用户数据(因为数据长度用 1 个字节表示) - 2 个字节的 CRC 校验,涵盖整个数据包,校验算法同 ModBus.

数据包与数据包之间要有一定的空闲时间,来隔开不同的数据包,详细请参见 CDBUS 的协议定义: https://github.com/dukelec/cdbus_ip

譬如地址 0x00 为主机,0x01 为 1 号从机,那么主机发送两个字节数据 0x10 0x11 给 1 号从机的完整数据为:

然后 1 号从机回覆单个 0x10 给主机:

然而 CDBUS 只是最底层的协议,接下来我们要定义上述用户数据的格式,最简单常用的方式就是首字节为命令号,然后后面跟可选命令参数; 回覆数据第一个字节通常为状态,然后是返回的数据。

这种方式完善之后也有一个名字,叫 CDNET, 它的定义在:https://github.com/dukelec/cdnet
(本文的内容这个连接都有包含,但本文会更加通俗的讲解一下关键细节。)

CDNET 协议有 3 个级别,由首字节的高两位决定:

实际使用根据情况自由选择某一个或某几个来用就好。

Level 0 格式
请求
首字节:

CDNET 的端口号可以看做类似电脑的 UDP 端口,也可以看做是一个命令号。

第二字节及其后:命令参数

回复
首字节:

例如: 回复 [0x40, 0x0c] 和回复 [0x6c] 是相同的意思。

首字节的用户数据(如果有)、第二个字节及其后:回复的状态 和/或 数据。

Level 1 格式
首字节:

MULTI_NET & MULTICAST

这个与本文主题无关,就不展开了。

SEQUENCE
0: 无序列号;
1: 追加 1 字节序列号 SEQ_NUM, 这个是重点,稍后会主要说明。

PORT_SIZE:

注: - 默认端口通常定为 0xcdcd, 所以不用额外追加字节. - 追加的字节按顺序,先是 src_port 再是 dst_port.

Level 2 格式
首字节:

注: - 使用分包功能的时候必须同时选择使用 SEQUENCE. - 开始分包的时候 SEQ_NUM 不需要归零.

一般情况下,要求不高,使用最简单的 Level 0 格式就好了,如果命令比较多,那么就可以用 Level 1 格式,用不到的功能不用理会即可。

Level 1 没有大数据分包功能,因为通常 MCU 也用不到那么大的数据包,即使是烧录代码这种要传大数据的功能,也是可以在命令内部定义地址和数据长度的,譬如我的 STM32 总线代码升级的命令定义:

而 Level 2 譬如可以用来传 IPv4/v6 数据包,那么就不得不加入拆包的功能了。因为 Level 1 和 Level 2 的序列号部分是一样的,所以接下来就混在一起讲了。

CDBUS 协议将前 0~9 保留专用,10 及其后的用户可以随便用,保留的部分目前也就用了 4 个,而且也不是强制的,用户愿意实现就实现,不用或者自己想怎么用就怎么用也没问题。 上篇文章说了端口或命令 0x01 是用来查询设备信息的,命令 0x03 是用来设置地址的,还详细说了如何使用这两个端口来实现地址自动分配,剩下两个端口其中 0x02 是用来设置波特率的, 对于本文最关键的端口 0x00 是用于流控、完整性确保、大数据拆包的了,其定义如下:

Port 0
配合 Level 1 和 2 头中的 SEQUENCE 字段使用。
命令启用 SEQUENCE 后追加的对应字节 SEQ_NUM[6:0] 的低 7 位会每次自动加 1.
而 SEQ_NUM 的第 7 位用来指示接收方是否要报告状态。
Port 0 本身的命令不可以启用 SEQUENCE.

Port 0 命令定义:

实际示例:
(-> 和 <- 是端口层的数据流, >> 和 << 是 CDNET 数据包层面的数据流,不含最低层的 CDBUS 的部分)

效率提升的重点就在这里,我们可以自行选择多久回复一次,而不是每次都要回复状态,如果最后一次数据包没有标注需要回复,那么会引发超时,然后主动读一次目标的 SEQ_NUM 以做同步。 之所以引发超时,是因为所有发出的数据包都不能立刻释放,要等确认对方收到才会释放,以防需要出错重传。 因为有 SEQ_NUM 号,所以即使同一个命令重复发送,对方也会只执行一次。

流控的功能也包含在内,譬如发送方时刻只允许最多 6 个数据包没有释放,那么等收到回复,释放掉 3 个,再发送 3 个数据包,这样可以最大化的利用总线带宽。 而且万一有多方发送数据至同一个节点,发送方也可以因频繁超时,来动态降低最大允许 pending 的数据包数量。

再来说大包拆分,也是很简单,拆分包有 3 个标记,分别是起始、继续、结束,譬如一个大包拆开了 4 个小包,且如果当前 SEQ_NUM 为 23,那么这四个小包的 SEQ_NUM 和标记对应关系就是:

这样接收方也就很容易的把四个小包还原成原始的大包,万一出问题,也只是重新传输错掉或丢掉的包(及其后的包)。

为了简便,对于 CDNET 协议,并不是丢一个包就只重传一个包,其后传的包也需要重传,因为接收方只是简单判断序号,不对便拒绝接收,这么做是为了保持简单,毕竟错包、丢包的概率很低。

最后,想说的是,这篇文章的内容都是经过实践检验的,我有用来传输摄像头视频,DEMO 可以在这篇介绍文章中看到:https://github.com/dukelec/cdbus_doc/blob/master/intro_zh.md

协议的实现部分代码也是开源的,就是上面的 CDNET 连接,另外有一些使用 CDNET 的示例代码,譬如这个 STM32F103 的步进电机控制器:https://github.com/dukelec/stepper_motor_controller

当然,这些代码、库我也会进一步优化改善。

参考链接


RS485 软件流控 及 数据完整性确保

完整教程:设计一款小巧但强大的传感器

由IEEE制定的新型单对以太网(SPE)或10BASE-T1L物理层标准,为传输设备运行状况信息实施状态监测(CbM)应用提供了新的连接解决方案。SPE提供共享电源和高带宽数据架构,可通过低成本双线电缆在超过1000米的距离实现10 Mbps数据和电源的共享。

ADI公司设计了业界首款10BASE-T1L MAC-PHY(ADIN1110),这是一款集成MAC的单对以太网收发器。ADIN1110使用简单的SPI总线与嵌入式微控制器通信,从而可降低传感器的功耗并减少固件开发时间。

在本文中,您将了解如何设计一款体型小巧但功能强大的传感器,如图1所示。本文将介绍:

● 如何设计小型共享数据和电源通信接口

● 如何为传感器设计超低噪声电源

● 微控制器和软件架构选择

● 选择合适的MEMS振动传感器

● 集成数字硬件设计和机械外壳

● 电脑上的数据采集UI示例

继续阅读完整教程:设计一款小巧但强大的传感器

【玩转ESP32】ESP32串口使用

1、ESP32串口

ESP32芯片有三个UART控制器(UART0, UART1UART2),其中UART0GPIO3用于U0RXDGPIO1用于U0TXD)用作下载、调试串口,引脚不可改变;

UART1UART2的引脚是可以设置的。 UART1默认引脚是GPIO9用作U1RXDGPIO10用作U1TXD,但是这两个引脚也是用于外接flash的,因此在使用UART1的时候需要设置其他引脚;

UART2默认引脚是GPIO16用作U2RXDGPIO17用作U2TXD

2、API

components/driver/include/driver/uart.h中可以查看api;

在examples/peripherals/uart中也可以参考官方的各种串口例程。

2.1、安装uart驱动

这里要注意参数:uart_queue属于freertos里面的队列句柄,在这里表示用于指示来自串口底层中断的队列消息。

2.2、uart参数配置

2.3、接收阈值设置

2.4、串口引脚设置

2.5、从接收缓冲区读取数据

2.6、数据写入发送缓冲区

3、代码实现

3.1、参数定义

3.2、串口初始化

3.3、串口任务函数

4、使用总结

  1. 初始化串口参数,(队列指针、缓冲区、引脚等);
  2. 任务中阻塞等待串口队列 。

如何实现 ESP32 固件的 OTA 在线升级更新

1、背景

在实际产品开发过程中,在线升级可以远程解决产品软件开发引入的问题,更好地满足用户需求。

2、OTA 简介

OTA(空中)更新是使用 Wi-Fi 连接而不是串行端口将固件加载到 ESP 模块的过程。

2.1、ESP32 的 OTA 升级有三种方式:
  • Arduino IDE:主要用于软件开发阶段,实现不接线固件烧写
  • Web Browser:通过 Web 浏览器手动提供应用程序更新模块
  • HTTP Server:自动使用http服务器 - 针对产品应用

在三种升级情况下,必须通过串行端口完成第一个固件上传。

OTA 进程没有强加的安全性,需要确保开发人员只能从合法/受信任的来源获得更新。更新完成后,模块将重新启动,并执行新的代码。开发人员应确保在模块上运行的应用程序以安全的方式关闭并重新启动。

2.2、保密性 Security

模块必须以无线方式显示,以便通过新的草图进行更新。 这使得模块被强行入侵并加载了其他代码。 为了减少被黑客入侵的可能性,请考虑使用密码保护您的上传,选择某些OTA端口等。

可以提高安全性的 ArduinoOTA 库接口:

已经内置了某些保护功能,不需要开发人员进行任何其他编码。ArduinoOTA和espota.py使用Digest-MD5来验证上传。使用MD5校验和,在ESP端验证传输数据的完整性。

2.3、OTA 升级策略 - 针对 HTTP

ESP32 连接 HTTP 服务器,发送请求 Get 升级固件;每次读取1KB固件数据,写入Flash。

ESP32 SPI Flash 内有与升级相关的(至少)四个分区:OTA data、Factory App、OTA_0、OTA_1。其中 FactoryApp 内存有出厂时的默认固件。

首次进行 OTA 升级时,OTA Demo 向 OTA_0 分区烧录目标固件,并在烧录完成后,更新 OTA data 分区数据并重启。

系统重启时获取 OTA data 分区数据进行计算,决定此后加载 OTA_0 分区的固件执行(而不是默认的 Factory App 分区内的固件),从而实现升级。

同理,若某次升级后 ESP32 已经在执行 OTA_0 内的固件,此时再升级时 OTA Demo 就会向 OTA_1 分区写入目标固件。再次启动后,执行 OTA_1 分区实现升级。以此类推,升级的目标固件始终在 OTA_0、OTA_1 两个分区之间交互烧录,不会影响到出厂时的 Factory App 固件。

3、OTA 实例解析

3.1、Arduino IDE方案固件更新

从 Arduino IDE 无线上传模块适用于以下典型场景:

在固件开发过程中,通过串行加载更快的替代方案 - 用于更新少量模块,只有模块在与 Arduino IDE 的计算机相同的网络上可用。

参考实例:

3.2、Web Browser 方案固件更新

该方案使用场景:

直接从 Arduino IDE 加载是不方便或不可能的

用户无法从外部更新服务器公开 OTA 的模块

在设置更新服务器不可行时,将部署后的更新提供给少量模块

参考实例:

3.3、HTTP 服务器实现更新

ESPhttpUpdate 类可以检查更新并从 HTTP Web 服务器下载二进制文件。可以从网络或 Internet 上的每个 IP 或域名地址下载更新,主要应用于远程服务器更新升级。

参考实例:

refer:

参考链接


esp32在Arduino 1.8下的I2C引脚

终于知道为什么esp的IO21和IO22引脚是SDA和SCL的默认接口。
恐惧:恐惧是给C++的,我这几天正好在网上看到别人说,C++语法可以特别特别恶心,常量特别特别难定位。我就在想有多难,有多恶心,CTRL+f很快就定位到了。然后这几天头头在捣鼓传感器,我就在旁边看着,学习学习。记下了当时他接的引脚号,回去自己捣鼓捣鼓,一般来说,接传感器都是要在程序里指定引脚的,我看了看样例代码里也没有指定就很纳闷(我也在头文件里面找,也没找到)。就问头头,为什么=这个不指定引脚啊?头头很随意的说“因为这个是使用I2C的接口,只需要指定传感器的编号(相当于人的手机号,每类传感器都有一个,也可以人为设定),esp32会自动扫描在总线上的设备,不用管接哪个引脚。”那我听了这话,就很感慨,原来现在传感器也这么智能了,就试了试其他的引脚,不行,我大概尝试了几十种组合方式,除了头头的那一种其他的都不行。这个时候也不好意思去问头头 ,感觉很简单的一个东西。于是就上网看看I2C协议,再看看一些实例,哦,原来随便接是说在同一个I2C接口上可以随便接很多个传感器,在这个I2C接口上会自动的扫描不同的传感器设备。好,那我们来看看esp32上的I2C接口是哪个引脚。如果不出所料应该是头头用的IO21,22

继续阅读esp32在Arduino 1.8下的I2C引脚

根据温度、气压计算海拔高度

有趣的是,这个简单的内容居然在国内的网站上很难搜索。不过无妨,毕竟是学过多年英语的人,链接在此。闲话不说,请看公式

1. hypsometric 公式

$ \Large h = \frac{[(\frac{P_0}{P})^{\frac{1}{5.257}}-1] \times (T + 273.15)}{0.0065} \tag{1}$

(1)

其中,$P_0$为标准大气压强,取值 $101.325 {\rm kPa}$; $P$为实际测量的大气压强,单位 ${\rm kPa}$; $T$为实际测量温度,单位 ℃。

上式中( T + 273.15 )是将摄氏度转化为华氏度。该公式同时考虑为温度和压强计算海拔高度,计算结果的单位是米。

2. barometric 公式

$\Large h = 44330 \times \left [ 1 - (\frac{P}{P_0})^{\frac{1}{5.255}} \right ] \tag{2}$

(2)

其实该公式只是只是(1)式在一定条件下取值,不考虑温度的影响,计算结果的单位是米。

继续阅读根据温度、气压计算海拔高度