最近发现了一个词:后手优势。在我刚刚学单片机的年代,受限于8051的性能,用库会被认为是浪费资源而不被推荐。即便是 printf 这种基础的函数也被建议替换为自己编写的简化版本。受限于这种思想,直到一两年前我都在使用最基础的寄存器封装库,例如 stm32f10x_stdperiph_lib。即便如此在一些要求苛刻的场合还会检查库函数的准确性,甚至直接操作寄存器,以避免出现不必要的bug。

大概在2015年的时候,第一次见识到了STM32 CUBE 的威力,连寄存器都不知道的软件助理竟能在一天的时间里完成记录串口数据至TF卡的要求。原来涉及TF卡的通信协议以及FAT文件系统都已经在 cube 中整理成库,只需要勾选即可自动生成工程文件,什么移植调试统统放一边。

但即便如此还是没让我在当时承认这种做法,直到后来 Arduino 和 Python 成为儿童玩具的时候我才真正意识到共享库的强大。在这个大部分时候机能过剩的时代,它能让几乎没有软件基础的人在数小时之内完成以前反复移植需要数天的工作。想想当时为了做音乐频谱甚至动用汇编优化的自己,真的是不堪回首的往事呢。

回到正题,由于路由器支持的 DDNS 服务商有限,这次希望 Arduino 实现一个 DDNS Client 的功能。作为初次尝试,考虑分步实现以下功能:

  1. NTP Client:获取当前时间
  2. Public IP 获取:由于 Arduino 连接在路由器后面,Local IP 并不是需要的公网IP。获取公网 IP 用于及时发现 IP 变化申请 DDNS 更新。
  3. DDNS Client:这才是需要的功能,最后发现和前两步没有任何关系,直接使用 http API 就可以直接实现。

这次选用的板子是一款以 ESP8266 为核心的 Arduino 开发板,主要考虑体积以及可以使用 WiFi 连接。同时这款开发板也集成了锂电池充电的功能,加上锂电池甚至可以独立运行。开发版外观及IO定义如下图:

在安装并配置好 Arduino 软件之后,导入 ESP8266 开发板的驱动库,就可以在示例中找到所需要实现功能对应的例程。但不得不说,这款开发板配套的例程错误率之高,竟然没有一个例程在填入 WiFi 密码后能直接使用。不过好在都是一些简单的错误,例如屏幕行数从 0~3,结果程序中出现一个第4行的设置导致无法显示等等。

事实证明我不擅长写详细的教学文,这里只提几个要点:

  1. NTP Client 需要开一个 UDP 端口并发送一个标准请求;
  2. HTTPS 使用443端口,需要对比指纹,网站指纹可以从 Chrome 中取得;
  3. Namecheap DDNS Client 可以直接通过 HTTPS GET 方式完成,参考:
    https://gist.github.com/t6/e48455f6ceed088ad619
    核心代码如下:

    out=$(${FETCH} "https://dynamicdns.park-your-domain.com/update?host=${host}&domain=${domain}&password=${password}")
    grep -q "<ErrCount>0</ErrCount>" <<EOF && exit 0
    
  4. TimeLib.h 用于 Unix 时间戳转换,既然用了 Arduino 那就把库发挥好,
    参考:https://playground.arduino.cc/code/time
    https://forum.arduino.cc/index.php?topic=37876.0
  5. 用了一个简单的非抢占式轮片调度,一方面应用非常简单不需要抢占,另一方面刚开始玩 Arduino 对于定时器中断使用还不了解,至于说为什么不写成 Class,因为懒 ……;
  6. 其他的直接看源码就好了,见附件。

最后看一下成果,从开始安装 Arduino IDE 开始算起也就1天时间。果然在这个机能过剩的时代合理用库才是王道。

源代码:DDNSClient.zip

TimeLib库:Time-master.zip

 

{ 本文链接: https://www.sy2k.com/2018/arduino-dabbling/;
原创文章, 转载请保留. 转载自 https://www.sy2k.com }