这篇文章来源于DevicePlus.com英语网站的翻译稿。
目录
1. 简介
2. 大致规格
3. ESP32 硬件概述
4. 将固件加载到 ESP32
5. 运行!!
6. 选择您自己的集线器:主机即为Heart.local所在的位置
7. 构建Wi-Fi 开关
1 简介
物联网技术已经深入到了大众生活中。宇宙大爆炸带来了世间所有存在的事物,但是以智能方式将它们连接起来,使其在发生相互关联的同时发挥出作用,是最近才取得的进展。几次雷击后,细胞在地球海洋的原始泥浆中做到了这一点,氨基酸为第一个细胞的形成构成了物质基础。然后就开始与其他万物合作共赢了吗?不,完全不是。细胞缺乏更高的智慧,它们只想在无尽的变异迭代中延续自己的生命。就这样,逐渐进化成了人类,才有了现在正在阅读有关物联网文章的您。
物联网的发展一定是以一个明确的目标开展的,并且在设计时会考虑未来产品的迭代。当然,您没有办法永生,只能提出一个想法,然后也许用几个月的时间来设计出原型。接下来,就是PCB设计了。这样说也许您会感到喜悦:每一个人都为物联网的发展作出了贡献,就像黎明前一个细胞加入了另一个细胞那样,总有一天会迎来人工智能的全面开花,而人工智能也许会在未来选择消灭人类,也可能不会。人工智能是基于二进制计算的。那么我们继续!
在本文中,您将会学习如何使用MicroPython和ESP32来制作一个可用于物联网项目的Wi-Fi开关。
2 大致规格
我需要一个本地物联网Wi-Fi开关,用于两个230V交流负载,每个负载高达500-600W。该开关需要易于更改(在代码中),需要可以安装在家里的任何地方,并且在硬件方面,可以快速进行修改和扩展。这意味着我必须使用MicroPython,以及一个ESP32开发板!它的引脚可以提供约10mA的电流,并且只需要串联一个330欧姆的电阻器就可以对其进行保护。所有的输出电压都为3.3V,对于现如今的MOSFET来说很完美,甚至可以驱动更高电压的负载。只要您确保使用逻辑电平MOSFET。WiFi传输速度约为150Mbit/s(18.75MByte/s),并且使用Loboris的ESP32固件(可在此处获取)可以节省更多时间,因为它已经包含了用于mDNS的库以及所有其他所需内容了。
mDNS可以使您的ESP32在网络上被发现(对于Wireshark爱好者,可以多播到224.0.0.251),并让您告诉其他使用者每个设备提供了哪些服务。它的系统与提供流媒体的媒体中心和Chromecasts的系统相同。我使用它来找到我的Raspberry Pi集线器(HEART.local),并且只接受来自该集线器的指令。
当使用USB TTL适配器连接到您的ESP32的UART时,您可以随时使用CTRL+C跳出正在运行的程序,然后查看变量,调整内存使用情况(micropython.meminfo(1), esp.freemem()),释放内存(gc.enable(), gc.collect()),进入粘贴模式(CTRL+E,paste, CTRL+D),从而了解一个想法是如何实现的。
想要在Linux、Windows、 以及MacOS X上使用图形界面,可以使用ampy(“ampy –port COM4 –put boot.py”)、mpfshell(mpfshell -o ttyUSB0 -nc “put boot.py ; put main.py”)或简单的ESPlorer。也就是说,如果您不单单只使用WebREPL(( ‘import webrepl’, ‘webrepl.start()’)来实现上传/下载代码以及查看代码运行结果的话,会需要这些程序包。为简单起见,您可以进行搜索相关信息,然后将MicroPython板连接到您的网络上。
3 ESP32 硬件概述
ESP32 规格
- CPU 双核@ 160/240MHz,Xtensa(R) 32位 LX6 微处理器
- 性能: <=600 DMIPS
- 芯片 ESP32D0WDQ6 (版本1)
- Wi-Fi:802.11/b/g/n/e/i – 802.11n @ 2.4GHz 高达150Mbit/s
- 蓝牙
- RAM:
- 448KB ROM 用于启动及核心功能
- 520KB 片上SRAM 用于数据及指令
- RTC(RTC FAST MEMORY)中的8KB SRAM用于从深度睡眠模式中恢复
- RTC(RTC SLOW MEMORY)中的8KB SRAM用于深度睡眠模式下的协同处理
- 1Kbit eFuse(256位用于系统(MAC和芯片配置)),768位用于用户应用程序、闪存加密以及芯片ID
- 40MHz晶振,可输出40MHz PWM
- 可用SPIRAM进行扩张!最多4个16MB外置QSPI 闪存/SRAM芯片,最多8MB映射到CPU数据空间的外置闪存/SRAM,支持存取8位、16位、32位数据。该SRAM可以写入:
- 闪存: 4MB SPI 闪存(在GPIO6、 GPIO7、GPIO8、GPIO9、GPIO10上)
- 安全性: IEEE 802.11 标准安全机制:
- WPA/WPA2/WAPI
- 安全启动
- 闪存加密
- 1024位OTP,最终用户可达768位
- AES,SHA-2,RSA,椭圆曲线加密(ECC),随机数生成(RNG)的加密硬件加速
ESP32 数据表
双核CPU和520KB RAM相对于您可能已经习惯使用的Arduino atmega328p (例如Uno)的16MHz和2KB SRAM有了质的提升,可以为您提供足够的速度和空间来开发更高复杂程度的物联网系统。相比于它的能力范围,我的Wi-Fi开关只使用了其中很少的功能。但是,我在ESP32的两侧都添加了引脚公头,之后可以随时为我构建的产品添加更多的功能。
wifiswitch.zip
4 将固件加载到 ESP32
您只需要esptool即可上传Loboris MicroPython。为了方便起见,请直接使用我的脚本:
[begin flash_lobo_esp32_all.sh]
# Enable flashing by holding the button to the right of
# microUSB port when powering ESP32 on
_baudrate=230400
_port=/dev/ttyUSB0
esptool.py --chip esp32 --before no_reset --after no_reset \
--baud $_baudrate --port $_port erase_flash
esptool.py --chip esp32 --port $_port --baud $_baudrate \
--before no_reset --after no_reset write_flash -z \
--flash_mode dio --flash_freq 40m --flash_size detect 0x1000 \
bootloader/bootloader.bin 0xf000 phy_init_data.bin \
0x10000 MicroPython.bin 0x8000 partitions_mpy.bin
[end flash_lobo_esp32_all.sh]
为了实现连接,您可以在Windows、 Linux、以及MacOS上使用终端指令“python3 -m serial.tools.miniterm”。Miniterm可以与python3-serial模块一起安装,或者可以直接下载putty软件来简化您的过程。根据您使用的Windows, Linux,以及MacOS系统,端口将会遵循不同的命名惯例。这两个程序都会要求您输入想要连接的端口以及波特率。对于MicroPython,使用115200。需要连接的端口在Windows系统上会有所不同(插入设备时在设备管理器中查找COM#端口变化),而在Linux或其他类似的系统中,通常对于第一个连接的USB串行适配器为“/dev/ttyUSB0”,第二个为“/dev/ttyUSB1”等等。
MicroPython的易用性和灵活性使您不会被代码困扰,从而能有更多的时间投入到电子产品的开发中。
5 运行!
您的ESP32在MicroPython上运行时,首先会执行“boot.py”,接下来是“main.py”(如果存在的话)。“boot.py”通常用于Wi-Fi的初始化,并且完成系统的准备工作。我将所有的指令都保存在了“boot.py”中,但是如果您原意的话,也可以将最后几个无限循环代码写入“main.py”中。如果您对Arduino比较熟悉的话,可以将“boot.py”看作“setup()”,将“main.py”看作“loop()”,只不过您需要添加一行“while True:”使“main.py”可以无限循环。
函数“connection_handler()”建立了一个Wi-Fi连接,会遍历“_wlans”元组中所有配置过的WLAN。如果无法连接,将会重新开始直到连接成功为止。该函数会每隔一段时间被调用一次。
遗憾的是,在本项目中无法调用“sta_if.status(‘rssi’)”,否则该函数将有助于把您的ESP32物联网设备放置在信号良好的位置。使用它您可以很方便地查看Wi-Fi信号强度。任何低于-80的信号都可能会导致间歇性的网络故障,而-65或更高强度的信号可以连接良好。
控制继电器的函数是“relay_handler()”,这是一个可以自我记录的ifthis-dothis-ifthat-dothat代码块。它会返回一串文本,来通知集线器它发送到ESP32的指令已经被成功执行。我们没有一个简单的状态机来对中继状态进行追踪,因为Loboris的ESP32模块可能无法使用“state = pin.value()”。您仍然可以添加状态追踪功能,但是需要使用“pin.value()”。我相信这样可以解决这个问题,因为它可以很完美地与官方MicroPython固件配合使用。
在设置mDNS并找到集线器(“HEART.local”)时,代码会不断尝试对集线器进行解析,直到成功为止。这旨在帮助您正确地设置Raspberry Pi。指令“avahi-browse -avtr”和“avahi-resolve –address/–name”会很有用!
最后,在大部分的有用信息被打印完成后,我们进入主循环部分。通过侦听10101/tcp端口,ESP32可以查看有效指令,并将符合要求的指令传递给“relay_handler()”。您可以禁用对“are-you-in-fact ‘HEART.local’?”的检查。简单地说,这种检查是无关紧要的,并不是真正的身份验证机制。mDNS网络名称是可以伪造的,并且随着时间的推移,集线器将会更改其IP地址。
最后有一个循环将会无限运行下去,该循环只与来自网络的输入有关。如果您想对其进行尝试,需要添加“except KeyboardInterrupt: break”,它包含在wifiswitch.zip中。
6 选择您的集线器:主机就是Heart.local所在的位置
对于您的集线器,我建议使用一个Raspberry Pi。查看有关如何设置Raspberry Pi的指南,并将其连接到网络上。对于无显示器操作,Raspberry Pi Zero W是理想选择,但是如果您想要通过图形界面进行操作,请使用Raspberry Pi 2/3/4 B/B+。当然,如果您运行的是Linux或其他类似的系统,也可以直接从您的笔记本电脑端来操作Wi-Fi开关。高级用户也可以尝试使用OpenWRT,它在新版路由器上的功能和Raspberry Pi 4 B一样,并且可以全天候运行。需要的软件包是avahi-daemon和avahi-discover(Linux或其他类似系统),配置示例请参阅wifiswitch.zip中的wifiswitch/rpi/etc/avahi/avahi-daemon.conf。如果您使用了一个Raspberry Pi,请确保在您的集线器上wifiswitch/rpi运行upgrade.sh和setup.sh,这样的话就可以保证一切正常。除了avahi-daemon,只需要再安装几个有用的额外的安装包。其中,“nmap”十分有用,它可以让您查看网络上的每个节点和服务。
avahi-daemon通常都预装在使用Linux发行版(如Ubuntu)的台式机和笔记本电脑上。如果没有的话,请使用您的软件包管理器将其添加到您的系统中。同时还必须安装avahi-discover包。在您的终端中,输入“sudo apt update; sudo apt install avahi-daemon avahi-discover”。然后,使用wifiswitch.zip中的wifiswitch/rpi/wifi_switch.sh来操作您的WiFiSwitch。您可以很轻松地对事务进行时间安排,而且如果您在外度假的话,cron可以在您期望的时间打开和关闭您家里的灯光,从而给小偷之类的人留下有人在家的印象。把卧室和客厅的灯以1秒钟的时间间隔进行打开和关闭?只有疯了的人才会做这种事吧。但是不要进行3-30Hz频率的循环,这样会导致一直执行重复性动作。
检查您的设置时,请使用“avahi-browse -avtr”查看所有的mDNS对等点及其服务。“avahi-browse -lavtr”将显示除了您所在系统之外的所有内容。使用“avahi-browse -avtr”查看所有服务是对设置进行调试的好方法!脚本“wifi_switch.sh”使用了指令“avahi-resolve –name WiFiSwitch.local”,通过指令“avahi-resolve –address”,您可以查找设备的mDNS名称。
[begin wifi_switch.sh]
#! /usr/bin/env bash
# <relay1|relay2|both> <on|off>
set -eu -o pipefail
# Usage: wifi_switch.sh < relay1 | relay2 | both > < on | off >
# Examples:
# wifi_switch.sh relay1 on
# wifi_switch.sh relay1 off
# wifi_switch.sh relay2 on
# wifi_switch.sh relay2 off
# wifi_switch.sh both on
# wifi_switch.sh both off
USAGE() {
echo "Usage: "$( basename $0)" < relay1 | relay2 | both > < on | off >"
cat <<-"EOF" Examples: wifi_switch.sh relay1 on wifi_switch.sh relay1 off wifi_switch.sh relay2 on wifi_switch.sh relay2 off wifi_switch.sh both on wifi_switch.sh both off EOF } if [ $# -ne 2 ] ; then USAGE ; exit ; fi NC=$( which nc 2>/dev/null ) ; if [ X"" = X"$NC" ] ; then \
echo "[!] No netcat in \$PATH? Exiting ..." ; exit ; fi
# avahi-resolve --name foo.local
# avahi-resolve --address 192.168.0.101 , etc.
AR=$( which avahi-resolve 2>/dev/null ) ; if [ X"" = X"$AR" ] ; then \
echo "[!] No avahi-resolve in \$PATH? Exiting ..." ; \
exit ; fi
SW_NAME='WiFiSwitch.local'
SW_ADDR=$( $AR --name $SW_NAME 2>/dev/null | awk \
'{print $2}' )
# Did $SW_NAME reply?
if [ -z $SW_ADDR ] ; then echo \
"[!] No reply from $SW_NAME - is the name correct? Is it online?" ; \
exit ; fi
SW_PORT=10101
# Check arguments
case $1 in
'relay1')
RELAY=$1
;;
'relay2')
RELAY=$1
;;
'both')
RELAY=$1
;;
*)
USAGE
exit
;;
esac
case $2 in
'on')
ACTION=$2
;;
'off')
ACTION=$2
;;
*)
USAGE
exit
;;
esac
# Remove these two lines once you have adapted this example script to your purposes
echo "INFO: Name=>$SW_NAME address=>$SW_ADDR port=>$SW_PORT relay=>$RELAY action=>$ACTION"
echo "COMMAND: echo $RELAY $ACTION | nc -w 1 $SW_ADDR $SW_PORT"
# The '-w' flag sets timeout in seconds
echo -n "REPLY: " ; echo "$RELAY $ACTION" | nc -w 1 $SW_ADDR $SW_PORT
echo
[end wifi_switch.sh]
以下是来自ESP32的日志,以及对来自我的集线器的指令的启动和执行。
[begin esp32.log]
...
D (1096) MicroPython: Main task exit, stack used: 1392
I (1098) MicroPython: [=== MicroPython FreeRTOS task started (sp=3ffc5c10) ===]
Internal FS (SPIFFS): Mounted on partition 'internalfs' [size: 1048576; Flash address: 0x200000]
----------------
Filesystem size: 956416 B
Used: 10752 B
Free: 945664 B
----------------
[!] Booting, running boot.py ...
I (2058) phy: phy_version: 3960, 5211945, Jul 18 2018, 10:40:07, 0, 0
[!] Hostname: WiFiSwitch.local
[!] Connecting to b'TestNetwork' using key b'TestPassword' ...
[!] Network configuration: ('192.168.0.100', '255.255.255.0', '192.168.0.1', '192.168.0.1')
[!] Starting mDNS ...
[!] Discovering control hub HEART.local ...
[!] HEART.local has address 192.168.0.101
[!] Entering listener loop, responding to arg1=>relay1|relay2|both arg2=>on|off - but only from HEART.local (192.168.0.101)
[!] relay1: OFF
[!] relay1: ON
[!] relay1 + relay2: OFF
[!] relay1 + relay2: ON
[!] Connected peer 192.168.0.103 is _NOT_ HEART.local (192.168.0.101), rejecting it ...
[!] Connected peer 192.168.0.103 is _NOT_ HEART.local (192.168.0.101), rejecting it ...
[!] relay1 + relay2: OFF
[!] relay1: ON
[!] relay1: OFF
[!] relay2: OFF
[!] relay2: ON
[!] relay2: OFF
[!] relay1 + relay2: ON
[end esp32.log]
在集线器上:
[begin hub.log]
$ # give wifiswitch a rigorous workout!
$ while true
do
wifi_switch.sh relay1 on ; sleep 2
wifi_switch.sh relay1 off ; sleep 2
wifi_switch.sh relay2 on ; sleep 2
wifi_switch.sh relay2 off ; sleep 2
wifi_switch.sh both on ; sleep 2
wifi_switch.sh both off ; sleep 2
done
INFO: Name=>WiFiSwitch.local address=>192.168.0.102 port=>10101 relay=>relay1 action=>on
COMMAND: echo relay1 on | nc -w 1 192.168.0.102 10101
REPLY: WiFiSwitch: relay1 ON
WiFiSwitch: OK
INFO: Name=>WiFiSwitch.local address=>192.168.0.102 port=>10101 relay=>relay1 action=>off
COMMAND: echo relay1 off | nc -w 1 192.168.0.102 10101
REPLY: WiFiSwitch: relay1 OFF
WiFiSwitch: OK
INFO: Name=>WiFiSwitch.local address=>192.168.0.102 port=>10101 relay=>relay2 action=>on
COMMAND: echo relay2 on | nc -w 1 192.168.0.102 10101
REPLY: WiFiSwitch: relay2 ON
WiFiSwitch: OK
INFO: Name=>WiFiSwitch.local address=>192.168.0.102 port=>10101 relay=>relay2 action=>off
COMMAND: echo relay2 off | nc -w 1 192.168.0.102 10101
REPLY: WiFiSwitch: relay2 OFF
WiFiSwitch: OK
INFO: Name=>WiFiSwitch.local address=>192.168.0.102 port=>10101 relay=>both action=>on
COMMAND: echo both on | nc -w 1 192.168.0.102 10101
REPLY: WiFiSwitch: relay1 + relay2 ON
WiFiSwitch: OK
INFO: Name=>WiFiSwitch.local address=>192.168.0.102 port=>10101 relay=>both action=>off
COMMAND: echo both off | nc -w 1 192.168.0.102 10101
REPLY: WiFiSwitch: relay1 + relay2 OFF
WiFiSwitch: OK
[end hub.log]
7 构建Wi-Fi开关
在构建设备时,您将需要以下部件。如果您没有这些部件,请查看零件数据表并使用具有相似特性的部件。例如,LM317是7805的完美替代品。
此外,您还需要一些粗绞线(剥出电源线,只需要约10厘米),也可以选择使用一个带研磨钻头的旋转工具。
假设您已经具有烙铁以及松香芯的焊料了,您可以使用任何具备温度控制功能的工具进行焊接。焊接温度最好为824华氏度,并且每次只接触焊点3-5秒。
该Fritzing 示意图显示了所有的连接。注意7805 Vout和ESP32之间的滑动开关。如果您想把ESP32和中继器板完全隔离开,请使用DPDT开关,并将其使用在5V和GND线上。光隔离器在正确操作的情况下不需要公共接地。您可以通过添加一个DPDT开关来免去一些麻烦,因为同时用7805和microUSB为ESP32供电是不可行的。
microUSB端口通常用于上传代码,microUSB端口上的5V引脚没有直接连接到5V引脚上,这里有一个正向偏置到该引脚的肖特基二极管。连接两个5V电源不仅会烧坏ESP32,可能还会损坏您的USB端口。我们永远不要让这种事情发生。只需在microUSB数据线连接到ESP32之前将滑动开关转到OFF位置即可。这不会影响开发板的操作,光隔离器与驱动继电器的MOSFET之间没有电流流通。您可以考虑购买一个USB TTL适配器,它可以让您连接到ESP32,并且无需在每次重启程序的时候进行重置。如果您通过其他方式为其供电,请勿将它的5V电线连接到开发板上。
如图所示将所有部件进行连接,请特别注意继电器上的电流端子。如果您有带磨头的旋转工具,请移除端子周围的铜垫,然后将粗绝缘线铺在清理过的表面上。开关继电器总是会产生电弧,但是这些电弧不会在继电器外产生。对板进行打磨。230V电压不会造成什么伤害,但是还是谨慎些为好。
完成后,您已经验证了继电器可以正常工作,在交流电流动的所有点以及这些点之间的线路上涂上具有耐高温特性的热胶,并粘上电工胶带。热胶在高达194华氏度的温度下形状稳定,所以它会一直稳固在其位置上。现在,我们已经确认了不会对身体造成伤害。
PC817光隔离器标有A-K、C-E。这意味着小圆标记旁边的引脚是阳极(A),旁边的引脚是阴极(K)。您需要在ESP32引脚和每个光隔离器阳极之间串联一个330欧姆的电阻。内部有一个红外线LED。您一定了解LED的一个特点—它们会在过高的电流负载下烧坏。发射的红外光打开了晶体管集电极(与NPN不太一样),使集电极(C)侧的电流流经发射极(E),到达2N7000 MOSFET栅极。发生这种情况时,2N7000将会打开,让来自继电器的电流流过漏极,通过源极流到GND,然后它会即刻发出咔哒声。
2N7000 MOSFET的连接很简单,但前提是您需要记得在栅极和GND之间放置10Kohm的下拉电阻。如果您忘记了这一点,栅极就不会被关闭,您的继电器也就无法可靠地转换。
您所构建的设备不需要很美观,它会被一直放在一个盒子里。该设备不会消耗太多功率(约2.7W),不过在外盒上添加几个小孔是个不错的主意,这样系统可以被动地进行冷却。如果没有这几个孔的话,热量聚集可能会使部件无法正常工作。
以下是一个类似的板,连接到了我的Raspberry Pi集线器上。它有自己的5V电源,并且仅依赖于4条3V3信号线和来自Pi的GND连接。虽然它看起来也不太美观,但是相比于具有相同容量和继电器的PCB,它的尺寸更小。
首先在面包板上测试您的电路,然后构建该电路。构建完成后,您可以开始根据自己的想法对代码进行调整。无论您有什么想法,我想都会比构建物联网鞋要好得多。我想我永远都没法搞明白为什么我用物联网购买了一些工具和书籍后,亚马逊会给我推荐这些产品。