程序员开发实例大全宝库

网站首页 > 编程文章 正文

Note-22.esp8266+micropython参考

zazugpt 2024-10-21 13:39:41 编程文章 16 ℃ 0 评论

ESP8266的资源非常有限(比如RAM内存)。因此请避免分配太大的容器对象(列表,字典)和缓冲区。

ESP8266也没完整的操作系统来跟踪资源并自动清理它们,所以请务必在使用后尽快关闭打开的文件、套接字等。


boot过程

启动时,MicroPython EPS8266在FlashROM中安装文件系统,执行_boot.py脚本,如果FlashROM不可用,则执行模块的首次安装并创建文件系统。该引导过程不能供普通用户定制。

一旦安装了文件系统,就执行boot.py。boot.py是在第一次设置模块时创建的,并具有启动WebREPL等守护进程的命令(默认禁用,可通过webrepl_setup模块进行配置),对boot.py文件的修改应该谨慎。

引导过程的最后一步,如果存在main.py,则从文件系统执行。这个文件是一个钩子,用于在每次引导时启动用户应用程序(而不是转到REPL)。对于小型测试应用程序,可以直接将它们命名为main.py,反之,建议将应用程序保存在单独的文件中,并在main.py中引入(import)使用。


已知问题

实时时钟(RTC):

  • ESP8266中的RTC精度很差,误差可能是秒/分钟。为了测量足够短的时间间隔,可以使用time.time()等函数,还可以使用附带的ntptime.py模块从网络同步时间。
  • 由于ESP8266芯片的限制,内部实时时钟(RTC)将每7:45小时溢出一次。如果需要长期工作的RTC时间,则必须在7小时内至少调用time()或localtime()一次,MicroPython将处理溢出。

同时操作STA_IF和AP_IF

  • 支持STA_IF和AP_IF接口同时操作。但是,由于硬件的限制,如果STA_IF没有连接和搜索到AP,AP_IF中可能存在性能问题。可以在只使用AP_IF的环境中禁用STA_IF

socket和WiFi缓冲区溢出

  • 套接字实例在显式关闭之前一直保持活动状态,这有两个问题。首先,它们会占用RAM,因此打开套接字而不关闭它们的应用程序最终可能会耗尽内存。其次,没有正确关闭socket会导致供应商WiFi堆栈的底层部分发出Lmac错误。如果数据进入套接字,但没有及时处理,就会发生这种情况。这可能会溢出WiFi堆栈输入队列并导致死锁,解锁的唯一方法是硬复位。
  • 上述情况也可能发生在应用程序因任何原因(包括异常)终止并退出REPL之后。随后的数据到达会触发失败,并重复发出上述错误消息。因此,套接字在任何情况下都应该关闭,无论应用程序是否成功终止或通过异常终止,例如使用try/finally:
sock = socket(...)
try:
    # Use sock
finally:
    sock.close()

SSL / TLS的局限性

ESP8266使用axTLS库,这是具有兼容许可的最小TLS库之一。然而,它也有一些已知的问题/限制:

  • 不支持DH (Diffie-Hellman)密钥交换和ECC (Elliptic-curve cryptography)。这意味着它不能与需要使用这些功能的站点一起工作(它可以与使用RSA证书的典型站点一起工作)。
  • 半双工通信性质。axTLS在发送和接收时都使用一个缓冲区,这样可以节省大量内存,并且可以很好地与HTTP等协议一起工作。但是,不遵循经典请求-响应模型的协议可能存在问题。

除了axTLS自身的限制外,MicroPython使用的配置还针对代码大小进行了高度优化,这导致了额外的限制(这些限制在未来可能会被取消):

  • 未启用优化的RSA算法,可能导致SSL握手速度变慢。
  • 没有启用会话重用,这意味着每个连接都必须经历完整的、昂贵的SSL握手。

除了上面描述的axTLS特定限制外,在低内存设备上使用TLS还有另一个一般限制:

  • TLS标准指定TLS记录(TLS通信的单位,整个记录必须在处理之前被缓冲)的最大长度为16KB。这几乎是可用ESP8266内存的一半,在一个或多或少高级的应用程序中,由于内存碎片问题,很难分配内存。折衷的办法是使用更小的缓冲区,因为SSL最有意思的用法是访问各种REST api,这通常需要更小的消息。缓冲区大小约为5KB,并不时进行调整,以作为参考

在MicroPython基于axTLS的ssl模块中,还有一些没有具体实现的特性:

  • 证书没有经过验证(这使得连接容易受到中间人攻击)。
  • 不支持客户端证书(计划在1.9.4发行版中修复)。

在ESP8266上使用mircoPython

烧录mircopython

  • 固件下载:http://micropython.org/download#esp8266
  • 使用esptool工具,需要使用pip或者下载:http://micropython.org/download#esp8266\
pip install esptool
esptool.py --port /dev/ttyUSB0 erase_flash
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=detect 0 esp8266-20170108-v1.8.7.bin
  • 验证固件
import esp
esp.check_fw()

串口提示

  • 一旦设备有了固件,就可以通过UART0 (GPIO1=TX, GPIO3=RX)访问REPL (Python提示符)。
  • 波特率为115200。

Wifi

  • 在安装好固件并启动后,设备将自己配置为可以连接的WiFi接入点(AP)。
  • ESSID的形式是MicroPython-xxxxxx,其中x被替换为设备MAC地址的一部分
  • WiFi的密码是micropythoN(注意大写N)。
  • ESP8266的IP地址是192.168.4.1。

REPL

  • REPL:Read Evaluate Print Loop
  • 有两种方式访问REPL:

通过UART串口的有线连接

  • REPL在UART0串行外设上始终可用,GPIO1用于TX的引脚和GPIO3用于RX引脚。
  • REPL的波特率为115200
  • 终端程序:thonny,picocom/mincom(linux)
picocom /dev/ttyUSB0 -b115200

通过WiFi使用WebREPL

import webrepl_setup
  • 在连接到WebREPL之前,需要先通过正常的串行连接启用并设置一个密码。

REPL使用

  • 行编辑Ctrl+A,HomeCtrl+E, End
  • 输入历史:ESP8266为8条历史记录
  • Tab自动补齐自动补齐模块查看模块功能,模块名称+".",tab键。
  • 黏贴模式:ctrl + E

其他控制命令

  • 在空行上按Ctrl-A将进入原始REPL模式。这类似于永久粘贴模式,只是字符不会回显。
  • 在空白的地方按Ctrl-B进入正常的REPL模式。
  • Ctrl-C取消任何输入,或中断当前运行的代码。
  • 在空行上按Ctrl-D将进行软重置。

文件系统

如果设备有1Mbyte或更多的存储空间,那么将被设置(在第一次引导时)包含一个文件系统。该文件系统使用FAT格式,存储在MicroPython固件之后的闪存中。

创建和读取文件:支持使用内置open()函数。

  • 默认文件打开为只读模式
f = open('data.txt')
f.read()
'some data'
f.close()
  • w:指定打开为可写模式
f = open('data.txt', 'w')
f.write('some data')
9
f.close()
  • wb:二进制写入模式
  • rb:二进制读取模式

列出文件:使用os模块

import os
os.listdir()
['boot.py', 'port_config.py', 'data.txt']
os.mkdir('dir')
os.remove('data.txt')

启动脚本

  • Boot.py:最先执行,执行一次
  • Main.py

网络基础

网络模块用于配置WiFi连接。有两个WiFi接口,一个用于工作站(当ESP8266连接到路由器时),另一个用于接入点(用于其他设备连接到ESP8266)。

import network
sta_if = network.WLAN(network.STA_IF)
ap_if = network.WLAN(network.AP_IF)

# 检查网络是否连接
sta_if.active()
False
ap_if.active()
True

# 查看网络信息
ap_if.ifconfig()
('192.168.4.1', '255.255.255.0', '192.168.4.1', '8.8.8.8')

对于新的ESP8266,默认配置为接入点模式,因此AP_IF接口是活动的,STA_IF接口是不活动的。可以将模块配置为使用STA_IF接口连接到现有网络。

# 激活sta模式
sta_if.active(True)
# 配置ssid和密码连接网络
sta_if.connect('<your SSID>', '<your key>')
# 验证网络是否正常连接
sta_if.isconnected()
# 查看网络信息
sta_if.ifconfig()
('192.168.0.2', '255.255.255.0', '192.168.0.1', '8.8.8.8')
# 可根据需要选择是否关闭AP模式
ap_if.active(False)
  • 自动连接wifi网络配置脚本,可存放与boot.py文件,启动即执行。
def do_connect():
    import network
    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print('connecting to network...')
        sta_if.active(True)
        sta_if.connect('<ssid>', '<key>')
        while not sta_if.isconnected():
            pass
    print('network config:', sta_if.ifconfig())

TCP套接字

大多数互联网的构建块是TCP套接字。这些套接字在连接的网络设备之间提供可靠的字节流。

  • 从网上下载数据
# 导入模块
import socket
# 获取服务器的IP地址:
addr_info = socket.getaddrinfo("towel.blinkenlights.nl", 23)
# getaddrinfo函数实际上返回一个地址列表,可选择第一个可用地址和端口
addr = addr_info[0][-1]
# 使用IP地址创建一个套接字并连接到服务器
s = socket.socket()
s.connect(addr)
# 下载并显示数据
while True:
    data = s.recv(500)
    print(str(data, 'utf8'), end='')
  • 如何下载网页。HTTP使用端口80,首先需要发送一个“GET”请求,指定要检索的页面。
# 定义函数
def http_get(url):
    import socket
    _, _, host, path = url.split('/', 3)
    addr = socket.getaddrinfo(host, 80)[0][-1]
    s = socket.socket()
    s.connect(addr)
    s.send(bytes('GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (path, host), 'utf8'))
    while True:
        data = s.recv(100)
        if data:
            print(str(data, 'utf8'), end='')
        else:
            break
    s.close()

# 执行函数
http_get('http://micropython.org/ks/test.html')
  • 简易HTTP服务器
import machine
pins = [machine.Pin(i, machine.Pin.IN) for i in (0, 2, 4, 5, 12, 13, 14, 15)]

html = """<!DOCTYPE html>
<html>
    <head> <title>ESP8266 Pins</title> </head>
    <body> <h1>ESP8266 Pins</h1>
        <table border="1"> <tr><th>Pin</th><th>Value</th></tr> %s </table>
    </body>
</html>
"""

import socket
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]

s = socket.socket()
s.bind(addr)
s.listen(1)

print('listening on', addr)

while True:
    cl, addr = s.accept()
    print('client connected from', addr)
    cl_file = cl.makefile('rwb', 0)
    while True:
        line = cl_file.readline()
        if not line or line == b'\r\n':
            break
    rows = ['<tr><td>%s</td><td>%d</td></tr>' % (str(p), p.value()) for p in pins]
    response = html % '\n'.join(rows)
    cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
    cl.send(response)
    cl.close()

GPIO引脚

不是所有引脚都是可用的,只有0, 2, 4, 5, 12, 13, 14, 15和16能够使用,GPIO16没有上拉模式。

# 导入machine模块
import machine
# 配置引脚输入或输出,以及是否开启内部上拉电阻(默认None)
pin = machine.Pin(0)
pin = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP)

# 读取引脚状态
pin.value()
0

# 设置引脚
pin = machine.Pin(0, machine.Pin.OUT)

pin.value(0)
pin.value(1)

pin.off()
pin.on()

外部中断:

除16引脚之外的所有引脚都可以配置为在输入改变时触发硬件中断,并可以设置要在触发器上执行的代码(回调函数)。

# 定义回调函数,用于处理中断事件
def callback(p):
...     print('pin change', p)

# 配置引脚
from machine import Pin
p0 = Pin(0, Pin.IN)
p2 = Pin(2, Pin.IN)

# 引脚0设置为仅在输入的下降沿上触发(当它从高到低时)
p0.irq(trigger=Pin.IRQ_FALLING, handler=callback)

# 引脚2设置为在上升沿和下降沿上触发
p2.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=callback)

# 可以将高电压和低电压应用到引脚0和2,以查看正在执行的中断。
  • 触发中断事件并调用回调函数进行处理
  • 硬件中断在事件发生时立即触发,并将中断任何正在运行的代码,包括Python代码。因此,回调函数所能做的事情是有限的(例如,它们不能分配内存),并且应该尽可能的简短。

PWM:Pulse Width Modulation,脉冲宽度调制脉宽调制

(PWM)是一种在数字引脚上获得人工模拟输出的方法。它通过快速切换引脚电压从低到高来实现这一点。

有两个参数与此相关:切换频率和占空比。占空比定义为高电压与单个周期(低时间加高时间)的长度比。最大占空比是指引脚一直为高电压,最小占空比是指引脚一直低电压。

在ESP8266上,引脚0、2、4、5、12、13、14和15都支持PWM。限制是这些引脚必须处于相同的频率,并且频率必须在1Hz和1kHz之间。创建引脚对象

# 创建PWM对象
import machine
p12 = machine.Pin(12)
pwm12 = machine.PWM(p12)
# 设置频率和占空比
pwm12.freq(500)
pwm12.duty(512)
# 注意,占空比在0(全关)和1023(全开)之间,512是50%的占空比。
# 超过这个最小/最大值的值将被剪切。打印PWM对象,
# 可查看当前配置
pwm12
PWM(12, freq=500, duty=512)
# 还可以调用不带参数的freq()和duty()方法来获取当前配置值。
# 引脚将保持在PWM模式,直到去初始化:
pwm12.deinit()

渐变(fade)LED

# 创建pwm引脚对象,并设置频率
led = machine.PWM(machine.Pin(2), freq=1000)
# 创建调制函数
import time, math
def pulse(l, t):
    for i in range(20):
        l.duty(int(math.sin(i / 10 * math.pi) * 500 + 500))
        time.sleep_ms(t)
# 调用
for i in range(10):
    pulse(led, 20)

控制舵机

  • 伺服电机可以使用PWM控制。要求频率为50Hz,占空比在40到115之间,以77为中心值。
servo = machine.PWM(machine.Pin(12), freq=50)
servo.duty(40)
servo.duty(115)
servo.duty(77)

ADC

ESP8266有一个单独引脚(与GPIO引脚分开),可用于读取模拟电压并将其转换为数字值。

# 创建ADC对象
import machine
adc = machine.ADC(0)
# 读取值
adc.read()
58
  • read()函数返回的值介于0(0.0伏)和1024(1.0伏)之间。请注意,此输入只能容忍最大1.0伏,必须使用分压器电路来测量更大的电压。



电源(功耗)控制

  • 动态改变CPU频率获取当前CPU频率
import machine
machine.freq()
80000000
  • 修改CPU频率为160MHz,可以在执行繁重的任务时更改为较高的频率,在完成处理时再更改回来用以控制功耗。
machine.freq(160000000)
machine.freq()
160000000
  • 深度睡眠状态深度睡眠模式将关闭ESP8266及其所有外设,包括WiFi(但不包括用于唤醒芯片的实时时钟),能够有效降低电流消耗。为了使用深度睡眠功能,必须将GPIO16连接到复位引脚(RST)。
import machine

# configure RTC.ALARM0 to be able to wake the device
rtc = machine.RTC()
rtc.irq(trigger=rtc.ALARM0, wake=machine.DEEPSLEEP)

# set RTC.ALARM0 to fire after 10 seconds (waking the device)
rtc.alarm(rtc.ALARM0, 10000)

# put the device to sleep
machine.deepsleep()
  • 注意,当芯片从深度睡眠中醒来时,它将被完全重置,包括所有的内存,boot脚本将照常运行。可以boot脚本其中放入代码以检查重置原因
if machine.reset_cause() == machine.DEEPSLEEP_RESET:
    print('woke from a deep sleep')
else:
    print('power on or hard reset')

控制DS18B20(1-wire)

1-wire总线是一种串行总线,它只使用一根线进行通信(除了地线和电源线)。DS18B20温度传感器是一种非常流行的单总线元件。将数据线连接到GPIO12,并在数据引脚和电源引脚之间连接一个4.7k欧姆电阻。

import time
import machine
import onewire, ds18x20

# the device is on GPIO12
dat = machine.Pin(12)

# create the onewire object
ds = ds18x20.DS18X20(onewire.OneWire(dat))

# scan for devices on the bus
roms = ds.scan()
print('found devices:', roms)

# loop 10 times and print all temperatures
for i in range(10):
    print('temperatures:', end=' ')
    ds.convert_temp()
    time.sleep_ms(750)
    for rom in roms:
        print(ds.read_temp(rom), end=' ')
    print()
  • 注意,必须执行convert_temp()函数来初始化温度读数,然后至少等待750ms才能读取该值。

控制NeoPixels(WS2812LEDs)

NeoPixels,也称为WS2812 led,是串行全彩led,可单独寻址,其红色、绿色和蓝色设置在0到255之间。创建NeoPixels对象(在GPIO4上配置一个8像素的NeoPixel灯条)

import machine, neopixel
# 定义在GPIO4上配置一个8像素的NeoPixel条带
np = neopixel.NeoPixel(machine.Pin(4), 8)
# 设置各像素的颜色
np[0] = (255, 0, 0) # set to red, full brightness
np[1] = (0, 128, 0) # set to green, half brightness
np[2] = (0, 0, 64)  # set to blue, quarter brightness
# 对于超过3种颜色的led,例如RGBW像素或RGBY像素,NeoPixel类采用bpp参数。
import machine, neopixel
np = neopixel.NeoPixel(machine.Pin(4), 8, bpp=4)
# 在4-bpp模式下,记得使用4元组而不是3元组来设置颜色。
np[0] = (255, 0, 0, 128) # Orange in an RGBY Setup
np[1] = (0, 255, 0, 128) # Yellow-green in an RGBY Setup
np[2] = (0, 0, 255, 128) # Green-blue in an RGBY Setup
# 使用write()方法将颜色输出到led
np.write()
# 示例
import time

def demo(np):
    n = np.n

    # cycle
    for i in range(4 * n):
        for j in range(n):
            np[j] = (0, 0, 0)
        np[i % n] = (255, 255, 255)
        np.write()
        time.sleep_ms(25)

    # bounce
    for i in range(4 * n):
        for j in range(n):
            np[j] = (0, 0, 128)
        if (i // n) % 2 == 0:
            np[i % n] = (0, 0, 0)
        else:
            np[n - 1 - (i % n)] = (0, 0, 0)
        np.write()
        time.sleep_ms(60)

    # fade in/out
    for i in range(0, 4 * 256, 8):
        for j in range(n):
            if (i // 256) % 2 == 0:
                val = i & 0xff
            else:
                val = 255 - (i & 0xff)
            np[j] = (val, 0, 0)
        np.write()

    # clear
    for i in range(n):
        np[i] = (0, 0, 0)
    np.write()
# 执行示例
demo(np)

控制APA102 LEDs

  • APA102 led,也称为DotStar led,是可单独寻址的全彩RGB led,通常以串形形式。与NeoPixels的不同之处在于,需要两个引脚来控制——一个时钟引脚和一个数据引脚。可以在比NeoPixels更高的数据和PWM频率下工作,并且更适合于视觉持久效果。创建APA102对象:配置一个60像素的APA102条带,时钟引脚GPIO5,数据引脚GPIO4。
import machine, apa102
strip = apa102.APA102(machine.Pin(5), machine.Pin(4), 60)
  • RGB颜色数据,以及亮度等级,以一定的顺序被发送到APA102。通常是(红,绿,蓝,亮度)。如果使用的是较新的ap102c led,绿色和蓝色顺序交换(红,蓝,绿,亮度)。如果希望以RGB顺序提供颜色,而不是RBG,可以自定义元组颜色顺序:
# 自定义元组颜色顺序
strip.ORDER = (0, 2, 1, 3)

# 设置像素颜色
strip[0] = (255, 255, 255, 31) # set to white, full brightness
strip[1] = (255, 0, 0, 31) # set to red, full brightness
strip[2] = (0, 255, 0, 15) # set to green, half brightness
strip[3] = (0, 0, 255, 7)  # set to blue, quarter brightness
# 使用write()方法将颜色输出到led
strip.write()
  • 示例
import time
import machine, apa102

# 1M strip with 60 LEDs
strip = apa102.APA102(machine.Pin(5), machine.Pin(4), 60)

brightness = 1  # 0 is off, 1 is dim, 31 is max

# Helper for converting 0-255 offset to a colour tuple
def wheel(offset, brightness):
    # The colours are a transition r - g - b - back to r
    offset = 255 - offset
    if offset < 85:
        return (255 - offset * 3, 0, offset * 3, brightness)
    if offset < 170:
        offset -= 85
        return (0, offset * 3, 255 - offset * 3, brightness)
    offset -= 170
    return (offset * 3, 255 - offset * 3, 0, brightness)

# Demo 1: RGB RGB RGB
red = 0xff0000
green = red >> 8
blue = red >> 16
for i in range(strip.n):
    colour = red >> (i % 3) * 8
    strip[i] = ((colour & red) >> 16, (colour & green) >> 8, (colour & blue), brightness)
strip.write()

# Demo 2: Show all colours of the rainbow
for i in range(strip.n):
    strip[i] = wheel((i * 256 // strip.n) % 255, brightness)
strip.write()

# Demo 3: Fade all pixels together through rainbow colours, offset each pixel
for r in range(5):
    for n in range(256):
        for i in range(strip.n):
            strip[i] = wheel(((i * 256 // strip.n) + n) & 255, brightness)
        strip.write()
    time.sleep_ms(25)

# Demo 4: Same colour, different brightness levels
for b in range(31,-1,-1):
    strip[0] = (255, 153, 0, b)
    strip.write()
    time.sleep_ms(250)

# End: Turn off all the LEDs
strip.fill((0, 0, 0, 0))
strip.write()

使用温湿度传感器DHT11

DHT(Digital Humidity & Temperature)数字温湿度传感器是低成本的数字传感器,带有电容式湿度传感器和热敏电阻,其芯片能够将模拟信号转换为数字,并提供单总线接口(或I2C接口)。

DHT11(蓝色)和DHT22(白色)传感器提供相同的1-wire接口,DHT22需要一个单独的对象,具有更复杂的计算。DHT22对湿度和温度读数都有1个小数位分辨率。DHT11则是整数。

自定义的1-wire协议(与Dallas 1-wire协议不同)用于从传感器获取测量数据。有效测量载荷由湿度值、温度值和校验和组成。使用1-wire接口,构造引用数据引脚对象

# DHT11
import dht
import machine
d = dht.DHT11(machine.Pin(4))
# DHT22
import dht
import machine
d = dht.DHT22(machine.Pin(4))

# 测量和读取
d.measure()
d.temperature()
d.humidity()
  • temperature()返回的值是摄氏度,humidity()返回的值是相对湿度的百分比。
  • DHT11每秒调用一次,DHT22每两秒调用一次。

在1-wire模式下,4个引脚中只有3个被使用,而在I2C模式下,所有4个引脚都被使用。旧的传感器可能仍然有4个引脚,即使它们不支持I2C,第三个引脚根本没有连接。

  • 引脚配置传感器没有I2C工作在1-wire模式(例如;Dht11, dht22, am2301, am2302):1=VDD, 2=Data, 3=NC, 4=GND
  • 传感器有I2C工作在1-wire模式(例如;Dht12, am2320, am2321, am2322):1=VDD, 2=Data, 3=GND, 4=GND
  • 传感器有I2C工作在I2C模式(例如。Dht12, am2320, am2321, am2322):1=VDD, 2=SDA, 3=GND, 4=SCL

数据、SDA和SCL引脚应该使用上拉电阻。

要使较新的I2C传感器工作在向后兼容的1-wire模式,必须将3和4引脚连接到GND,用以禁用I2C接口。

DHT22传感器现在以AM2302的名称出售,在其他方面是相同的。


使用SSD1306 OLED

SSD1306 OLED显示器使用SPI或I2C接口,有多种尺寸(128x64, 128x32, 72x40, 64x48)和颜色(白色,黄色,蓝色,黄色+蓝色)。

  • 硬件SPI接口:
from machine import Pin, SPI
import ssd1306

hspi = SPI(1)  # sck=14 (scl), mosi=13 (sda), miso=12 (unused)

dc = Pin(4)    # data/command
rst = Pin(5)   # reset
cs = Pin(15)   # chip select, some modules do not have a pin for this

display = ssd1306.SSD1306_SPI(128, 64, hspi, dc, rst, cs)
  • 软件SPI接口:
from machine import Pin, SoftSPI
import ssd1306

spi = SoftSPI(baudrate=500000, polarity=1, phase=0, sck=Pin(14), mosi=Pin(13), miso=Pin(12))

dc = Pin(4)   # data/command
rst = Pin(5)  # reset
cs = Pin(15)  # chip select, some modules do not have a pin for this

display = ssd1306.SSD1306_SPI(128, 64, spi, dc, rst, cs)
  • I2C接口
from machine import Pin, I2C
import ssd1306

# using default address 0x3C
i2c = I2C(sda=Pin(4), scl=Pin(5))
display = ssd1306.SSD1306_I2C(128, 64, i2c)
  • 在第一行打印Hello World:
display.text('Hello, World!', 0, 0, 1)
display.show()
  • 基本功能
display.poweroff()     # power off the display, pixels persist in memory
display.poweron()      # power on the display, pixels redrawn
display.contrast(0)    # dim
display.contrast(255)  # bright
display.invert(1)      # display inverted
display.invert(0)      # display normal
display.rotate(True)   # rotate 180 degrees
display.rotate(False)  # rotate 0 degrees
display.show()         # write the contents of the FrameBuffer to display memory
  • 子类FrameBuffer提供了对基本图形的支持:
display.fill(0)                         # fill entire screen with colour=0
display.pixel(0, 10)                    # get pixel at x=0, y=10
display.pixel(0, 10, 1)                 # set pixel at x=0, y=10 to colour=1
display.hline(0, 8, 4, 1)               # draw horizontal line x=0, y=8, width=4, colour=1
display.vline(0, 8, 4, 1)               # draw vertical line x=0, y=8, height=4, colour=1
display.line(0, 0, 127, 63, 1)          # draw a line from 0,0 to 127,63
display.rect(10, 10, 107, 43, 1)        # draw a rectangle outline 10,10 to 117,53, colour=1
display.fill_rect(10, 10, 107, 43, 1)   # draw a solid rectangle 10,10 to 117,53, colour=1
display.text('Hello World', 0, 0, 1)    # draw some text at x=0, y=0, colour=1
display.scroll(20, 0)                   # scroll 20 pixels to the right

# draw another FrameBuffer on top of the current one at the given coordinates
import framebuf
fbuf = framebuf.FrameBuffer(bytearray(8 * 8 * 1), 8, 8, framebuf.MONO_VLSB)
fbuf.line(0, 0, 7, 7, 1)
display.blit(fbuf, 10, 10, 0)           # draw on top at x=10, y=10, key=0
display.show()
  • 示例:绘制MicroPython标志并显示一些文本信息:
display.fill(0)
display.fill_rect(0, 0, 32, 32, 1)
display.fill_rect(2, 2, 28, 28, 0)
display.vline(9, 8, 22, 1)
display.vline(16, 2, 22, 1)
display.vline(23, 8, 22, 1)
display.fill_rect(26, 24, 2, 4, 1)
display.text('MicroPython', 40, 0, 1)
display.text('SSD1306', 40, 12, 1)
display.text('OLED 128x64', 40, 24, 1)
display.show()

常用模块和功能参考

machine模块

import machine

machine.freq() # 获取CPU的当前频率

machine.freq(160000000) # 设置CPU频率为160 MHz

esp模块

import esp

esp.osdebug(None) # 关闭 O/S 调试信息

esp.osdebug(0) # 重定向 O/S 调试信息到 UART(0)

network模块

import network

# STA模式

wlan = network.WLAN(network.STA_IF) # 创建STA接口

wlan.active(True) # 激活接口

wlan.scan() # 扫描AP

wlan.isconnected() # 检查STA是否连接到AP

wlan.connect('ssid', 'key') # 指定SSID和密码连接AP

wlan.config('mac') # 获取接口MAC地址

wlan.ifconfig() # 获取接口网络信息,包括IP/netmask/gw/DNS

# AP模式

ap = network.WLAN(network.AP_IF) # 创建AP接口

ap.active(True) # 激活接口

ap.config(ssid='ESP-AP') # 设置AP的SSID

# STA模式网络连接函数

def do_connect():

import network

wlan = network.WLAN(network.STA_IF)

wlan.active(True)

if not wlan.isconnected():

print('connecting to network...')

wlan.connect('ssid', 'key')

while not wlan.isconnected():

pass

print('network config:', wlan.ifconfig())

time模块

import time

time.sleep(1) # 睡眠1秒

time.sleep_ms(500) # 睡眠500毫秒

time.sleep_us(10) # 睡眠10微秒

start = time.ticks_ms() # 获取毫秒计数器

delta = time.ticks_diff(time.ticks_ms(), start) # 计算时间差

timers:支持基于rtos的虚拟定时器。使用ID为-1的machine.Timer定时器类,周期单位为毫秒。

from machine import Timer

tim = Timer(-1)

tim.init(period=5000, mode=Timer.ONE_SHOT, callback=lambda t:print(1))

tim.init(period=2000, mode=Timer.PERIODIC, callback=lambda t:print(2))

Pins和GPIO:使用machin.Pin类

  • 可用引脚为:0、1、2、3、4、5、12、13、14、15、16
  • 注意,引脚(1)和引脚(3)分别是REPL UART的TX和RX
  • Pin(16)是一个特殊的Pin(用于从深度睡眠模式唤醒),可能无法用于更高级别的类,如Neopixel。
  • 高级抽象类machine.Signal用于翻转引脚电平,可用于低电平激活的LED。
from machine import Pin

p0 = Pin(0, Pin.OUT) # 定义GPIO0为输出引脚

p0.on() # 设置引脚为高电平

p0.off() # 设置引脚为低电平

p0.value(1) # 设置引脚为高电平

p2 = Pin(2, Pin.IN) # 定义GPIO2为输入引脚

print(p2.value()) # 获取引脚的电平,1为高电平,0为低电平

p4 = Pin(4, Pin.IN, Pin.PULL_UP) # 定义GPIO5为输入,并启用内部上拉电阻

p5 = Pin(5, Pin.OUT, value=1) # 定义GPIO5输出高电平

UART(串口总线):两个uart可用。

  • UART0在引脚1 (TX)和3 (RX)上。UART0是双向的,默认情况下用于REPL
  • UART1位于引脚2 (TX)和8 (RX)上,但引脚8用于连接闪存芯片,因此UART1仅为TX。
  • 当UART0附加到REPL时,UART(0)上的所有传入字符都会直接进入stdin,因此uart.read()将总是返回None。
  • 如果需要从UART(0)中读取字符,则使用sys.stdin.read(),同时它也用于REPL(或分离,读取,然后重新连接)。分离后,UART(0)可以用于其他目的。
from machine import UART

uart = UART(0, baudrate=9600)

uart.write('hello')

uart.read(5) # 读取最多5个字节

# 从REPL分离UART0

import os

os.dupterm(None, 1)

# 重新连接REPL

import os, machine

uart = machine.UART(0, 115200)

os.dupterm(uart, 1)

PWM(pulse width modulation):脉冲宽度调制

  • PWM可以在除引脚(16)以外的所有引脚上启用。
  • 所有通道都有一个单一频率,范围在1到1000之间(以Hz为单位)。
  • 占空比介于0和1023之间。
from machine import Pin, PWM

pwm0 = PWM(Pin(0)) # 对指定引脚创建PWM对象

pwm0.freq() # 获取当前频率

pwm0.freq(1000) # 设置频率

pwm0.duty() # 获取当前占空比

pwm0.duty(200) # 设置占空比

pwm0.deinit() # 关闭引脚PWM功能

pwm2 = PWM(Pin(2), freq=500, duty=512) # 创建PWM对象并指定频率和占空比

ADC (analog to digital conversion):模数转换

  • ADC在专用引脚上可用。
  • 注意:ADC引脚上的输入电压必须在0v到1.0v之间。
from machine import ADC

adc = ADC(0) # 在ADC引脚创建ADC对象

adc.read() # 读取ADC的值,范围0-1024

软件SPI总线:

  • 是通过软件实现的(bit-banging),可以在所有引脚上工作,并通过machine.SoftSPI类访问。
from machine import Pin, SoftSPI

# 在特定引脚构造SPI总线

# polarity参数标识SCK idle状态的电平

# phase=0 标识从SCK的第一个边沿开始采样 , phase=1标识从SCK的第二个边沿开始采样

spi = SoftSPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4))

spi.init(baudrate=200000) # 设置波特率

spi.read(10) # 从 MISO读取10字节

spi.read(10, 0xff) # 在MOSI上输出0xff同时读取10个字节

buf = bytearray(50) # 创建缓冲区

spi.readinto(buf) # 读入给定的缓冲区(buf前面只当为50个字节)

spi.readinto(buf, 0xff) # 读入给定的缓冲区,并在MOSI上输出0xff

spi.write(b'12345') # 在MOSI上写5个字节

buf = bytearray(4) # 创建缓冲区

spi.write_readinto(b'1234', buf) # 写入到MOSI,并从MISO读入缓冲区

spi.write_readinto(buf, buf) # 将buf写入MOSI,并将MISO读回buf

硬件SPI总线

  • 硬件SPI速度更快(高达80Mhz),但只能在特定引脚上工作:MISO是GPIO12, MOSI是GPIO13, SCK是GPIO14。和软件SPI具有相同的方法,除了构造函数和init的引脚参数(因为它们是固定的):
from machine import Pin, SPI

hspi = SPI(1, baudrate=80000000, polarity=0, phase=0)

I2C总线

  • I2C总线是软件实现的,可以在所有引脚上工作,并通过machine.I2C类(machine.SoftI2C的别名)访问
from machine import Pin, I2C

# 构造 I2C 总线

i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)

i2c.readfrom(0x3a, 4) # 从地址为0x3a的外设读取4个字节

i2c.writeto(0x3a, '12') # 向地址为0x3a的外设写入“12”

buf = bytearray(10) # 创建10字节缓冲区

i2c.writeto(0x3a, buf) # 对指定的外设写入缓冲区的数据

RTC(Real time clock)实时时钟

  • machine.RTC类方法中RTC.now(), RTC.irq(handler=*)(使用自定义处理程序),RTC.init()和RTC.deinit()目前不支持。
from machine import RTC

rtc = RTC()

rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # 设置日期时间

rtc.datetime() # 获取日期时间

# 连接wifi,使用ntp同步时间

ntptime.settime() # 设置从远程服务器同步时间

rtc.datetime() # 获取 UTC日期时间

WDT(watchdog timer)看门狗

  • machine.WDT
from machine import WDT

# 启用 WDT

wdt = WDT()

wdt.feed()

深度睡眠模式

  • 将GPIO16连接到复位引脚(RST)。
import machine

# 配置使用RTC.ALARM0唤醒设备

rtc = machine.RTC()

rtc.irq(trigger=rtc.ALARM0, wake=machine.DEEPSLEEP)

# 检查设备是否是从深度睡眠模式唤醒

if machine.reset_cause() == machine.DEEPSLEEP_RESET:

print('woke from a deep sleep')

# 设置RTC.ALARM0 在10秒后唤醒设备

rtc.alarm(rtc.ALARM0, 10000)

# 使设备进入深度睡眠模式

machine.deepsleep()

单总线(OneWire)

  • OneWire是软件实现的,可以在所有引脚上工作
from machine import Pin

import onewire

ow = onewire.OneWire(Pin(12)) # 在 GPIO12引脚创建单总线

ow.scan() # 返回单总线上连接的设备

ow.reset() # 重置总线

ow.readbyte() # 读取一个字节

ow.writebyte(0x12) # 向总线特定地址设备写入一个字节

ow.write('123') # 向总线写入字节

ow.select_rom(b'12345678') # 根据ROM代码选择特定的设备
  • DS18S20和DS18B20设备驱动程序:确保数据线连接4.7k的上拉电阻注意,每次要对温度进行采样时都必须调用convert_temp()方法。
import time, ds18x20

ds = ds18x20.DS18X20(ow)

roms = ds.scan()

ds.convert_temp()

time.sleep_ms(750)

for rom in roms:

print(ds.read_temp(rom))

NeoPixel

  • 默认情况下,NeoPixel被配置为控制更流行的800kHz设备。可以在构造NeoPixel对象时,通过传递timing=0,使用替代计时来控制其他(通常是400kHz)设备。
  • NeoPixel的底层驱动,参见machine.bitstream。
from machine import Pin

from neopixel import NeoPixel

pin = Pin(0, Pin.OUT) # 设置GPIO0 输出到 NeoPixels

np = NeoPixel(pin, 8) # 在GPIO0上创建8灯珠的NeoPixel

np[0] = (255, 255, 255) # 设置第一个灯珠为白色

np.write() # 写入数据到所有灯珠

r, g, b = np[0] # 获取第一个灯珠的颜色

APA102

from machine import Pin

from apa102 import APA102

clock = Pin(14, Pin.OUT) # 设置 GPIO14 为时钟输出

data = Pin(13, Pin.OUT) # 设置 GPIO13 为数据输出

apa = APA102(clock, data, 8) # 创建8灯珠APA102

apa[0] = (255, 255, 255, 31) #设置第一个灯珠为白色,最大亮度为31

apa.write() # 写入数据到所有灯珠

r, g, b, brightness = apa[0] # 获取第一个灯珠的颜色
  • APA102底层控制
import esp

esp.apa102_write(clock_pin, data_pin, rgbi_buf)

DHT

  • DHT驱动是软件实现的,可以在所有引脚上工作:
import dht

import machine

d = dht.DHT11(machine.Pin(4))

d.measure()

d.temperature() # 摄氏度

d.humidity() # 湿度百分比

d = dht.DHT22(machine.Pin(4))

d.measure()

d.temperature() # 摄氏度

d.humidity() # 湿度百分比

SSD1306 OLED

from machine import Pin, I2C

import ssd1306

i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)

display = ssd1306.SSD1306_I2C(128, 64, i2c)

display.text('Hello World', 0, 0, 1)

display.show()

WebREPL(web browser interactive prompt)

  • 初始化webrepl配置
import webrepl_setup
  • 如果未设置启动时自动启动,可手动启动
import webrepl

webrepl.start()

其他

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表