Easy MicroPython 教程与手册

简介

几年前,上海乐鑫公司开发了一个ESP8266芯片。该芯片的定位目标是低成本WiFi物联网解决方案,但是除了WiFi外,该芯片也自带了基础的逻辑计算和针脚输入输出的功能,类似于一个自带WiFi的Arduino。

然而纯粹的ESP8266芯片需要自己焊接针脚和板子,也只能用C语言进行开发,不太方便。后来有人创建了一个新项目:NodeMCU,该项目将ESP8266进行了简单的封装,同时开发了支持Lua语言的框架,自此ESP8266可以使用Lua脚本语言来编程。

再之后,澳大利亚程序员和物理学家Damien George开发的MicroPython,开始支持ESP8266,自此之后,就可以在ESP8266的设备(如NodeMCU)中刷入MicroPython框架,用Python来对NodeMCU进行编程。

也就是说,用20元左右,就可以打造一套用Python编写的,能够联网的Arduino。

但是MicroPython本身只完成了一些基础的功能,对于不同的传感器等操作并没有进一步的封装,如果想使用MicroPython去控制一些复杂的传感器,会需要一定的专业功底。

为此,我创建了Easy MicroPython项目。这是在MicroPython之上的一层封装,将针脚操作和传感器、控制器连接进行了进一步的简化,目的是为了让初学者更加方便、快捷地使用MicroPython。

本项目目前仅在基于ESP8266或ESP32的NodeMcu上进行过调试,其余硬件不保证兼容性。

安装与快速开始

首先你需要一块NodeMCU板,推荐基于ESP32的,功能更强大,基于ESP8266的也可以使用大部分的功能。

之后将你的NodeMCU板刷入MicroPython框架,具体方法可以查看 MicroPython官网

本框架本质上是一些封装好函数的py文件,放置于NodeMCU的根目录即可正常使用。

项目提供了一个自动刷入的程序,如果你对NodeMCU不熟悉,可以跟着以下步骤直接使用:

  1. 下载本项目:https://github.com/RainGather/Easy_MicroPython/releases

  2. 解压到一个目录中,例如:C:\nodemcu,请保证整个路径没有空格和中文,同时请务必放在C盘(放在其它盘符可能会导致未知的错误)

  3. 安装Python 3,同时请将Python路径加入到环境变量PATH中

  4. 将NodeMCU连接上电脑

  5. 双击flash.bat运行,第一次运行时,会自动尝试安装缺失的库文件,安装完后需关闭重新打开。

  6. 按指示选择端口和设备型号,等待框架刷入完成后关闭窗口。

  7. 在该目录下,新建HelloWorld.py,在里面可以写入本手册中的案例代码。例如:

    print('Hello World')
    
  8. 双击upload.bat

  9. 按照指示选择端口和需刷入的文件,文件会自动列出在列表中,填写数字序号即可。

  10. 显示刷入成功后,按下[CTRL + D]或板子上的[RST]键重启板子运行。

  11. 如果一切正常,刷入窗口最后应该会出现Hello World字样。

  12. 窗口关闭后,可用用connect.bat重新连接至NodeMCU。

约定

中英文

为了方便中文使用者,本项目中多种函数、类都有对应的中文拼音名可用,两者效果是一致的。

通用模板

以下为通用模板:

from ezmpy import *

wifi('WIFI名称', 'WIFI密码')  # 可选,连接wifi,默认会同时连接test.mosquitto.org用作MQTT服务器

# 在这里写你的代码

run()  # 开始执行循环守护进程

如果没有from ezmpy import *或run(), 系统在烧录时会自动加上。

基础

针脚图

ESP8266:

base/nodemcu_8266_pin.jpg

ESP32:

_images/nodemcu_32s_pin.png

输出高/低电平

用如下代码即可让针脚输出高电平:

p = OUT(0)  # 0可以换成其它针脚,OUT、O、Out_三者同名同义
p.on()

用如下代码即可让针脚输出低电平:

p = OUT(0)  # 0可以换成其它针脚
p.off()

获取高/低电平

用如下代码即可获取指定针脚的电平:

p = IN(0)  # 可以将0换成其它针脚,IN、I、In_三者同名同义
v = p.value()  # 将当前0针脚的电平值赋给v变量,高电平为1,低电平为0

PWM输出

PWM为脉冲宽度调制输出,可以理解成释放出最高电压一定百分比的电压。 并不是所有的针脚都支持PWM输出,其中

ESP8266:针脚D0, 2, 4, 5, 12, 13, 14 和 15支持PWM模拟输出
ESP32:GPIOs 0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 18 ,19, 21, 22, 23, 25, 26, 27, 32, 33支持

MicroPython中,PWM的范围为0-1023.

可以用如下代码来产生PWM:

p = PWM(2)  # 2针脚用于PWM输出
p.duty(512)  # 占空比设置为512/1024

模拟输入

NodeMCU的ESP8266版本,只有一个A0模拟输入,其官方的读取方式也很简单,就没有再度封装:

from machine import ADC
a = ADC(0)
a.read()

舵机

将舵机信号线连到支持PWM的针脚。 一般常见的舵机,可以用如下代码直接控制:

d = SERVO(2)  # 针脚2连接舵机信号线,可以交换
d.turn(90)  # 转到90度。

一般舵机理论上转角为0-180度,但由于舵机的精度问题,尽量建议只使用30-150度。

如果有某些特殊的舵机,控制PWM频率是不同的,例如频率为440HZ,可以用如下代码控制:

d = SERVO(2, freq=440)
d.turn(90)

为了便于母语中文的初学者使用,SERVO与DUOJI做了映射,zhuan与turn做了映射,用DUOJI、zhuan代替上述的SERVO、turn词汇,也可以正常使用。

WiFi与物联网(MQTT)

用如下代码即可将NodeMCU连接至网络:

wifi('wifiname', 'wifipwd')  # 请将内容改成自己的WiFi名和密码,注意不要有引号、中文等特殊字符,尽量纯英文+数字

连接网络后,即可使用urequests库,具体方法请查看 urequests源代码

本项目已集成MQTT,可以方便的基于MQTT开发物联网设备。

MQTT是一种针对物联网优化过的协议,其结构很简单,为’主题-内容’对应式结构,下面用一个例子来说明:

假设C1是放置于机房的温度传感器物联网设备,C2是放置于办公室的机房过热报警灯。 那么C1可以发布一个主题,名为’computer_room/temperature’,值为当前温度。 C2可以订阅一个主题,名字与上述C1名一致。 这样一旦’computer_room/temperature’这个主题发生改变,C2就可以立刻获取改变后的值。获取值以后,可以判断其是否高于某个温度,从而决定是否发出警报。

默认情况下,一旦连接wifi,就会连接上test.mosquitto.org的服务器,该服务器是公共服务器,如您只是测试使用,可用此服务器,如正式使用,请务必切换到安全的服务器。

用如下代码可以指定自己的MQTT服务器和验证方式:

wifi('wifiname', 'wifipwd', False)  # 需要在连接WiFi时加入False参数,否则会直接连接到test.mosquitto.org服务器。
mqtt_init(host='www.yoursite.org', port=1883, user='yourusername', pwd='youruserpwd')  # 如果你的mqtt服务器没有验证,则可以不填写mqtt_user与mqtt_pwd

发布主题:

pub('topic/can/split/like/path', 'str content')  # 注意必须使用str类型,如果是数字类型请自行转换

主题只能用英文,消息如果包含中文,需要进行编码:

msg = '中文消息'
pub('topic/can/split/like/path', msg.encode('utf-8'))

订阅主题,并设置触发后的操作:

@sub('a/topic')  # 主题可以模糊订阅,用+表示一个级数,#表示多个级数
def whatevername(topic, msg):  # 一旦订阅的主题发生改变,此函数就会接受发生改变的主题topic和消息msg
    # 接受到的topic和msg都是bytes类型,需要用decode转成字符串类型
    topic = topic.decode('utf-8')
    msg = msg.decode('utf-8')
    print(topic, msg)

定时器

用来定时执行某项任务,用法为:

timer(delta_time, func)

delta_time输入间隔时间,func输入需要运行的函数,该函数无法加参数。

全局变量

由于封装的原因,普通的global可能无法正常使用,可以用g变量,该变量本身是一个字典类型,可以把要保存的数据存于该变量中。

例如:

g['save'] = 'test'

这样在其它地方调用:

print(g['save'])

就可以了。

时间相关

NodeMCU没有内置的时间,只提供一个time.time()函数获取已经通电了多少秒:

import time
print(time.time())  # 结果会是类似42,意思是已经通电42秒

为此本框架内置了NTP对时系统,如果你使用WIFI函数连接了网络,框架会尝试自动进行NTP对时,如果一切正常,使用time.time()就可用获取当前的时间戳(由于MicroPython的设置,时间戳是从2000年开始的,不是Linux的1970年开始):

import time

WIFI('wifiname', 'wifipwd')
print(time.time())  # 结果会是类似527648123这样的数字,意思是当前时间与2000年1月1日0时所差的秒数。
print(time.localtime())  # 结果会是类似(2018, 4, 16, 15, 59, 03, 0, 106), 意思是2018年4月16日15:59:03,最后两位数0和106暂时没用

由于网络原因,即使WIFI连接了,NTP对时也可能失败,可用用如下代码手工对时:

if not ntp_ok():  # 检测ntp是否已经成功校准,已校准的话返回True,否则返回False
    set_ntp_time(tz=8)  # tz为时区,北京时区为正8区,所以tz=8,如果是-6区就写tz=-6

非阻塞循环

如果采用如下方式进行循环:

while True:
    print('hello world')
print('这一条永远不会被执行')

会产生阻塞,导致最下面的print命令永远不会被执行。

所以Easy MicroPython集成了一个非阻塞式的循环,用如下方式即可:

@loop
def any_name():  # 这个函数名可以任取
    print('hello world1')

print('这条不会被阻塞,会执行')

@loop
def any_name2():
    print('hello world2')

最终输出为:

这条不会被阻塞,会执行
hello world1
hello world2
hello world1
hello world2
hello world1
hello world2
...

传感器

超声波传感器

注意:超声波传感器的基础供电电压至少为5V,NodeMCU一般只能供给3.3V,所以需要自己搭建电路,否则不会正常工作!!!!

如下代码可以通过超声波传感器获取距离:

# 将ULTRASONIC换成CHAOSHENGBO同效
u = ULTRASONIC(1, 2)  # 1是超声波探测器上的Echo_Pin脚所接的针脚号,2是超声波探测上的Trig_Pin脚所接的针脚号。部分厂商的针脚是相反的,如果无法正常使用,两者换一下再试试看。
u.get()  # 获取距离,单位为米

DHT11温湿度传感器

DHT11是一种常见的温湿度传感器,淘宝上有售。 正确连接DHT11的电源线,并将DHT11的信号线,连入NodeMCU的任一数据口(在此处以D2口为例),用如下代码获取温湿度:

d = DHT11(2)  # 2为温湿度传感器信号所连针脚
wendu, shidu = d.get()  # 同时获取温度和湿度

DHT22温湿度传感器

DHT22是一种常见的温湿度传感器,淘宝上有售。 正确连接DHT22的电源线,并将DHT22的信号线,连入NodeMCU的任一数据口(在此处以D2口为例),用如下代码获取温湿度:

d = DHT22(2)  # 2为温湿度传感器信号所连针脚
wendu, shidu = d.get()  # 同时获取温度和湿度

微雪(waveshare)指纹传感器

由于该指纹传感器需要串口通讯,目前而言并不太适合在基于ESP8266的NodeMCU上使用,因为ESP8266除了USB连接的UART0口,只提供了一个供TX的UART1口,并没有RX口。故而其实现很复杂。如确实想在8266版本的NodeMCU上使用,请查看源码自行修改。

将指纹传感器的电源线接好,将RX端接到SD3(GPIO10),将TX端接到SD2(GPIO9)。

声明对象:

f = FINGER()

如需设定超时,用如下方式声明:

f = FINGER(timeout=10)  # 将FINGER换成ZHIWEN也同效果

指纹录入:

f.add()  # 运行后会要求连续按压3次指纹,返回True代表录入成功,False代表录入失败,可能是指纹上有异物,重试即可

已保存的指纹数量:

count = f.count()  # 已保存的指纹数量赋值给count变量

删除所有保存的指纹:

f.delete()

匹配指纹:

f.match_finger()  # 如果当前指纹和已保存的指纹有相符的,会返回保存指纹的ID(ID必定>0),否则返回0或False

由于指纹匹配时,处于阻塞状态,无法进行任何其它操作,故而可以人工异步匹配:

import time

f = FINGER()
f.ready()  # 指纹模块进入识别状态
while True:
    result = f.match()
    if result == 0:
        print('Finger Error!')  # 匹配错误,指纹不是已录入指纹
        f.ready()  # 重新进入识别状态进行比对
    if result is None:
        print('Waiting for Finger!')  # 指纹还处于识别状态,还没有手指放上去让识别
    if result:
        print('Right!')
    time.sleep(0.1)

代码案例

控制LED开关

将LED灯的正极接D0脚,负极接GND,用如下代码就可以控制LED的开关。

开灯:

# 设置针脚0为输出端,你也可以选择其他的针脚
p = OUT(0)
# 将针脚0设为高电平,这样连接针脚0的LED灯就会发光
p.on()

关灯:

# 设置针脚0为输出端,你也可以选择其他的针脚
p = OUT(0)
# 将针脚0设为低电平,这样连接针脚0的LED灯就会灭了
p.off()

LED闪烁

将LED连接到针脚0,随后可以用如下代码控制:

# 导入time库,可以使用等待等功能
import time


# 设置针脚0为输出端
p = OUT(0)


while True:
    # 将针脚0设为高电平,这样连接针脚0的LED灯就会发光
    p.on()
    # 等待1秒
    time.sleep(1)
    # 将针脚0设为低电平,这样连接针脚0的LED灯就会灭了
    p.off()
    # 等待1秒
    time.sleep(1)

呼吸灯

将LED正极连接到针脚2,用如下代码可以控制LED灯变成呼吸灯:

# 导入时间库,可以使用等待功能
import time


# 声明2针脚为PWM输出端,在ESP8266中针脚0, 2, 4, 5, 12, 13, 14 和 15支持PWM模拟输出
p = PWM(2)


while True:
    # 将指针2的输出,从0逐渐增长到1024,0为最低输出(0V),1024为最高输出(5V或3.3V)
    for i in range(1024):
        # 设置针脚输出
        p.duty(i)
        # 等待0.005秒,防止闪烁过快
        time.sleep(0.005)
    # 将指针2的输出,从1024逐渐减少到0
    for i in range(1024, 0, -1):
        # 设置针脚输出
        p.duty(i)
        # 等待0.005秒,防止闪烁过快
        time.sleep(0.005)

控制舵机角度作为雨刮器

将舵机信号线接到针脚2,并用如下代码控制舵机在30度与150度之间循环旋转:

import time


# 声明2针脚连接舵机的信号线,针脚1-8支持舵机控制,如舵机不是50HZ的,可以用SERVO(2, freq=频率)的方式来声明
servo = SERVO(2)


while True:
    # 将舵机旋转30度,普通舵机理论上可以转0-180度,但由于舵机制作的精度原因,建议控制在30-150度左右旋转
    servo.turn(30)
    time.sleep(2)
    # 将舵机旋转150度,普通舵机理论上可以转0-180度,但由于舵机制作的精度原因,建议控制在30-150度左右旋转
    servo.turn(150)
    time.sleep(2)

获取按钮是否按下

将按钮分别将针脚0连接到GND和VCC,并用如下代码获取针脚0连接的是GND还是VCC:

# 导入时间库,这样可以使用等待的功能
import time


# 将针脚0作为数字输入
p = IN(0)


while True:
    # 获取针脚0上输入的电平高低,如果为高则为1,如果为低则为0
    i = p.value()
    # 将其值打印出来
    print(i)
    # 等待1秒,防止刷新太快
    time.sleep(1)

获取温度传感器的值

将DHT11或DHT22的信号线连接到针脚2,用如下代码获取温湿度的值:

import time


# 2针脚连DHT11温湿度传感器的数据端,如果是DHT22请将下列代码中的DHT11改成DHT22
d = DHT11(2)


while True:
    # 一口气获取温度和湿度
    wendu, shidu = d.get()
    # 打印温度到窗口
    print(wendu)
    # 打印湿度到窗口
    print(shidu)
    # 等待1秒
    time.sleep(1)

指纹识别

本代码使用的是微雪指纹模块,其余指纹模块若想使用封装,可以私信联系我。

由于本指纹识别模块是串口通讯,而ESP8266由于其本身设计原因,无法简单通过UART0之外的串口来进行通讯,故而本代码只适合用于ESP32为核心的NodeMCU。

请将指纹模块的RX端接到SD3(GPIO10),将TX端接到SD2(GPIO9):

f = FINGER()
f.add_finger()  # 添加指纹,会录入3次,3次正常则返回True,否则返回False
print(f.match_finger())  # 进行指纹比对,如果当前指纹在指纹库中存在,则返回其ID(ID必定大于0),否则返回0或者False

具体可以查看:微雪(waveshare)指纹传感器

串口模式

介绍

注意!!!!!!

串口模式一定要使用5V供电,3.3V的供电会导致串口出问题最终影响使用!!!!

再注意!!!!!!!

发送命令时,请等待几秒,例如连接好WiFi后,请等待5秒再发送下一个命令。否则可能导致命令被忽略!

由于某些原因,ESP8266的刷入在某些电脑中会出现各种问题。目前我暂时无法排查出问题在什么地方,同时为了方便大家在自己熟悉的环境中使用物联网,故而做了串口控制的功能。

Nodemcu的串口接收使用UART(0)口,发送使用UART(1)口,默认波特率为115200,在ESP8266板子中,接受就是RX口,发送就是D4口。接电路的时候需要将Nodemcu的rx、d4分别与上位机的tx、rx相接,同时需要共地。

串口控制的统一格式为:

[cmd|arg|arg|arg]

由于串口通讯某些时候会有干扰字符,故而使用尖括号括起命令的格式传输。例如,mqtt的订阅格式为:

[sub|title/subtitle]

这样在之后接收到mqtt的消息后,ESP8266会返回:

[sub|title/subtitle|msg]

请避免在arg或cmd中含有竖杠,目前没有做自动转义,如需竖杠符号在msg或arg中存在,请在上位机中自行完成转义工作。

命令发送后,如一切正常则会返回:

[sta|ok]

如果命令有误,则会返回:

[sta|nocmd]

串口MQTT控制

注意!!!!!!

串口模式一定要使用5V供电,3.3V的供电会导致串口出问题最终影响使用!!!!

再注意!!!!!!!

发送命令时,请等待几秒,例如连接好WiFi后,请等待5秒再发送下一个命令。否则可能导致命令被忽略!

连接Wifi:

[wif|wifiname|wifipwd]

如果WiFi连接错误会自动退出,返回:

[sta|exit]

设定服务器(默认为test.mosquitto.org,你可以自行搭建一个服务器,也可以使用他人的服务器,例如:https://www.cloudmqtt.com/,需注册):

[svr|test.mosquitto.org|1883|user|pwd]

其中端口号、用户名和密码可以为空:

[svr|test.mosquitto.org]
[svr|test.mosquitto.org|1883]

订阅主题:

[sub|title/subtitle]

主题有消息时会返回一条消息(请自行完成监听):

[sub|title/subtitle|msg]

发送消息:

[pub|title/subtitle|msg]

以上命令都会在成功执行后返回:

[sta|ok]

如需重启或重置,可以发送命令:

[sys|reboot]

会收到命令:

[sys|rebooting]

手机端MQTT连接推荐: https://apkpure.com/linear-mqtt-dashboard/com.ravendmaster.linearmqttdashboard