• 【MicroPython ESP32】ssd1306 0.96“OLED+气象数据中文显示


    【MicroPython ESP32】ssd1306 0.96“OLED+气象数据中文显示


    ⚡本篇硬件ESP32所使用的是特定的固件包, 包含GB2312字库驱动,如果硬件所使用的microPython官方的固件并不适合本篇所讲的内容。请参照上面提供的相关篇内容进行了解和固件刷写。
    • 📚固件资源和字库
    链接:https://pan.baidu.com/s/11tq0UUur2Zspt0VvcBTYpg 
    提取码:it1b
    
    • 1
    • 2

    在这里插入图片描述
    在这里插入图片描述

    📑开启这一篇内容前,说说调试经历,调试过程中遇到了一个很大的坑,折腾了一天。山穷水尽疑无路,人总需要抱一丝希望,万一成功了呢?遇到困难需要变通方法才行,否则容易被困于死胡同,找不到解答。

    • 🌿调试bug:ImportError: no module named 'parse'

    在引入模块import urequests时,老是出现ImportError: no module named 'parse',出现这个问题,结果查阅,Python使用import语句访问modules,需要指定从中导入Parse的路径,了解文件夹层次结构非常重要。

    • 🔧思路参考:https://www.codenong.com/46211002/
    🚩原固件本身始是基于uPyCraft平台开发的使用的,MicroPython设备根目录下带lib文件夹,import urequests模块在MicroPython官方的固件中是已经封装到固件里面的,而这个带字库支持的固件却没有,但是有关代码中应该是又有指定了路径,所以在调用import urequests模块时,不仅需要上传到MicroPython设备上,而且需要指定路径。

    📝接线说明

    • 🎋所使用的是0.96" I2C OLED屏幕
    scl=Pin(22)
    sda=Pin(21)
    
    • 1
    • 2

    ⛳心知天气API接口

    • 📍心知天气:https://www.seniverse.com/
      在这里插入图片描述

    🎉免费版的使用次数足够一般的需求了。

    https://api.seniverse.com/v3/weather/now.json?key=your_api_key&location=beijing&language=zh-Hans&unit=c
    
    • 1
    • your_api_key:填写自己申请的Key(在使用我的代码时,请不要使用我保留下来的Key
      在这里插入图片描述
    • beijing:换成自己的城市(拼音)
    def fetchWeather(location):
        result = urequests.get("https://api.seniverse.com/v3/weather/now.json?key=S9hoa4Wza9Hcs2uX_&location=shenzhen&language=zh-Hans&unit=c")       
        return result.text
    
    • 1
    • 2
    • 3
    • 填写个人的wifi信息
    SSID ="MERCURY_D268G" # 填写个人wifi信息
    PASSWORD ="pba5ayzk"
    
    • 1
    • 2

    🍁文件结构

    • 所需的字库文件,固件资源下载地址:https://github.com/kaixindelele/ssd1306-MicroPython-ESP32-Chinese/(已在上面提供了百度网盘资源)
      在这里插入图片描述
    • urequests.py内容:
    import usocket
    
    class Response:
    
        def __init__(self, f):
            self.raw = f
            self.encoding = "utf-8"
            self._cached = None
    
        def close(self):
            if self.raw:
                self.raw.close()
                self.raw = None
            self._cached = None
    
        @property
        def content(self):
            if self._cached is None:
                try:
                    self._cached = self.raw.read()
                finally:
                    self.raw.close()
                    self.raw = None
            return self._cached
    
        @property
        def text(self):
            return str(self.content, self.encoding)
    
        def json(self):
            import ujson
            return ujson.loads(self.content)
    
    
    def request(method, url, data=None, json=None, headers={}, stream=None):
        try:
            proto, dummy, host, path = url.split("/", 3)
        except ValueError:
            proto, dummy, host = url.split("/", 2)
            path = ""
        if proto == "http:":
            port = 80
        elif proto == "https:":
            import ussl
            port = 443
        else:
            raise ValueError("Unsupported protocol: " + proto)
    
        if ":" in host:
            host, port = host.split(":", 1)
            port = int(port)
    
        ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM)
        ai = ai[0]
    
        s = usocket.socket(ai[0], ai[1], ai[2])
        try:
            s.connect(ai[-1])
            if proto == "https:":
                s = ussl.wrap_socket(s, server_hostname=host)
            s.write(b"%s /%s HTTP/1.0\r\n" % (method, path))
            if not "Host" in headers:
                s.write(b"Host: %s\r\n" % host)
            # Iterate over keys to avoid tuple alloc
            for k in headers:
                s.write(k)
                s.write(b": ")
                s.write(headers[k])
                s.write(b"\r\n")
            if json is not None:
                assert data is None
                import ujson
                data = ujson.dumps(json)
                s.write(b"Content-Type: application/json\r\n")
            if data:
                s.write(b"Content-Length: %d\r\n" % len(data))
            s.write(b"\r\n")
            if data:
                s.write(data)
    
            l = s.readline()
            #print(l)
            l = l.split(None, 2)
            status = int(l[1])
            reason = ""
            if len(l) > 2:
                reason = l[2].rstrip()
            while True:
                l = s.readline()
                if not l or l == b"\r\n":
                    break
                #print(l)
                if l.startswith(b"Transfer-Encoding:"):
                    if b"chunked" in l:
                        raise ValueError("Unsupported " + l)
                elif l.startswith(b"Location:") and 300 < status < 400:
                    new_url = (l[10:-2]).decode('utf8')
                    return request(method, new_url, data, json, headers, stream)
        except OSError:
            s.close()
            raise
    
        resp = Response(s)
        resp.status_code = status
        resp.reason = reason
        return resp
    
    
    def head(url, **kw):
        return request("HEAD", url, **kw)
    
    def get(url, **kw):
        return request("GET", url, **kw)
    
    def post(url, **kw):
        return request("POST", url, **kw)
    
    def put(url, **kw):
        return request("PUT", url, **kw)
    
    def patch(url, **kw):
        return request("PATCH", url, **kw)
    
    def delete(url, **kw):
        return request("DELETE", url, **kw)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • ssd1306.py内容
    # MicroPython SSD1306 OLED driver, I2C and SPI interfaces
    
    from micropython import const
    import framebuf
    
    
    # register definitions
    SET_CONTRAST = const(0x81)
    SET_ENTIRE_ON = const(0xA4)
    SET_NORM_INV = const(0xA6)
    SET_DISP = const(0xAE)
    SET_MEM_ADDR = const(0x20)
    SET_COL_ADDR = const(0x21)
    SET_PAGE_ADDR = const(0x22)
    SET_DISP_START_LINE = const(0x40)
    SET_SEG_REMAP = const(0xA0)
    SET_MUX_RATIO = const(0xA8)
    SET_COM_OUT_DIR = const(0xC0)
    SET_DISP_OFFSET = const(0xD3)
    SET_COM_PIN_CFG = const(0xDA)
    SET_DISP_CLK_DIV = const(0xD5)
    SET_PRECHARGE = const(0xD9)
    SET_VCOM_DESEL = const(0xDB)
    SET_CHARGE_PUMP = const(0x8D)
    
    # Subclassing FrameBuffer provides support for graphics primitives
    # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
    class SSD1306(framebuf.FrameBuffer):
        def __init__(self, width, height, external_vcc):
            self.width = width
            self.height = height
            self.external_vcc = external_vcc
            self.pages = self.height // 8
            self.buffer = bytearray(self.pages * self.width)
            super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB, self.width)
            self.init_display()
    
        def init_display(self):
            for cmd in (
                SET_DISP | 0x00,  # off
                # address setting
                SET_MEM_ADDR,
                0x00,  # horizontal
                # resolution and layout
                SET_DISP_START_LINE | 0x00,
                SET_SEG_REMAP | 0x01,  # column addr 127 mapped to SEG0
                SET_MUX_RATIO,
                self.height - 1,
                SET_COM_OUT_DIR | 0x08,  # scan from COM[N] to COM0
                SET_DISP_OFFSET,
                0x00,
                SET_COM_PIN_CFG,
                0x02 if self.width > 2 * self.height else 0x12,
                # timing and driving scheme
                SET_DISP_CLK_DIV,
                0x80,
                SET_PRECHARGE,
                0x22 if self.external_vcc else 0xF1,
                SET_VCOM_DESEL,
                0x30,  # 0.83*Vcc
                # display
                SET_CONTRAST,
                0xFF,  # maximum
                SET_ENTIRE_ON,  # output follows RAM contents
                SET_NORM_INV,  # not inverted
                # charge pump
                SET_CHARGE_PUMP,
                0x10 if self.external_vcc else 0x14,
                SET_DISP | 0x01,
            ):  # on
                self.write_cmd(cmd)
            self.fill(0)
            self.show()
    
        def poweroff(self):
            self.write_cmd(SET_DISP | 0x00)
    
        def poweron(self):
            self.write_cmd(SET_DISP | 0x01)
    
        def contrast(self, contrast):
            self.write_cmd(SET_CONTRAST)
            self.write_cmd(contrast)
    
        def invert(self, invert):
            self.write_cmd(SET_NORM_INV | (invert & 1))
    
        def show(self):
            x0 = 0
            x1 = self.width - 1
            if self.width == 64:
                # displays with width of 64 pixels are shifted by 32
                x0 += 32
                x1 += 32
            self.write_cmd(SET_COL_ADDR)
            self.write_cmd(x0)
            self.write_cmd(x1)
            self.write_cmd(SET_PAGE_ADDR)
            self.write_cmd(0)
            self.write_cmd(self.pages - 1)
            self.write_data(self.buffer)
    
    
    class SSD1306_I2C(SSD1306):
        def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
            self.i2c = i2c
            self.addr = addr
            self.temp = bytearray(2)
            self.write_list = [b"\x40", None]  # Co=0, D/C#=1
            super().__init__(width, height, external_vcc)
    
        def write_cmd(self, cmd):
            self.temp[0] = 0x80  # Co=1, D/C#=0
            self.temp[1] = cmd
            self.i2c.writeto(self.addr, self.temp)
    
        def write_data(self, buf):
            self.write_list[1] = buf
            self.i2c.writevto(self.addr, self.write_list)
    
    
    class SSD1306_SPI(SSD1306):
        def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
            self.rate = 10 * 1024 * 1024
            dc.init(dc.OUT, value=0)
            res.init(res.OUT, value=0)
            cs.init(cs.OUT, value=1)
            self.spi = spi
            self.dc = dc
            self.res = res
            self.cs = cs
            import time
    
            self.res(1)
            time.sleep_ms(1)
            self.res(0)
            time.sleep_ms(10)
            self.res(1)
            super().__init__(width, height, external_vcc)
    
        def write_cmd(self, cmd):
            self.spi.init(baudrate=self.rate, polarity=0, phase=0)
            self.cs(1)
            self.dc(0)
            self.cs(0)
            self.spi.write(bytearray([cmd]))
            self.cs(1)
    
        def write_data(self, buf):
            self.spi.init(baudrate=self.rate, polarity=0, phase=0)
            self.cs(1)
            self.dc(1)
            self.cs(0)
            self.spi.write(buf)
            self.cs(1)
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157

    📝主程序代码

    from lib import urequests
    #import urequests as requests
    from machine import Pin,I2C
    i2c = I2C(1,scl=Pin(22), sda=Pin(21),freq=100000)
    from ssd1306 import SSD1306_I2C #从ssd1306模块中导入SSD1306_I2C子模块
    import time
    import network
    import ujson
    
    #import ssd1306
    #import HZK
    
    #spi = SPI(baudrate=100000, polarity=1, phase=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19))
    #cs = Pin(25,Pin.OUT)
    #HZK.init(spi,cs)
    
    oled = SSD1306_I2C(128, 64, i2c,addr=0x3c) #OLED显示屏初始化:128*64分辨率,OLED的I2C地址是0x3c
    
    # 心知天气API申请:https://seniverse.yuque.com
    # API_KEY = 'S9hoa4Wza9Hcs2uX_'
    #接口;https://api.seniverse.com/v3/weather/now.json?key=your_api_key&location=beijing&language=zh-Hans&unit=c
    # LOCATION = 'zhuzhou'
    # URL = 'https://api.seniverse.com/v3/weather/now.json'
    # UNIT = 'c'
    # LANGUAGE = 'zh-Hans'
    
    SSID ="MERCURY_D268G" # 填写个人wifi信息
    PASSWORD ="pba5ayzk"
    wlan=None
    s=None
    #连接网络
    
    def connectWifi(ssid,passwd):    
        
        global wlan
        wlan=network.WLAN(network.STA_IF)
        wlan.active(True)
        wlan.disconnect()
        wlan.connect(ssid,passwd)
        
        while(wlan.ifconfig()[0]=='0.0.0.0'):        
            time.sleep(1)
        
        return True
    
    def fetchWeather():
        result = urequests.get("https://api.seniverse.com/v3/weather/now.json?key=S9hoa4Wza9Hcs2uX_&location=shenzhen&language=zh-Hans&unit=c")       
        return result.text
    
    if __name__ == '__main__':        
        oled.font_load("GB2312-12.fon")# 所使用的字体时12号字体
        oled.fill(0)
        oled.text("ESP32 Weather Clock", 4, 0)
        oled.line(0, 14, 128, 14, 1)#划线
        connectWifi(SSID,PASSWORD)
        result = fetchWeather()
        print(result)
        j=ujson.loads(result)
        print("\r\n\r\n")
        print(j['results'][0]['location']['name'])
        print(j['results'][0]['now']['text'])
        print(j['results'][0]['now']['temperature'])
        addr=j['results'][0]['location']['name']
        weather=j['results'][0]['now']['text']
        temperature=j['results'][0]['now']['temperature']
        oled.text("地点:%s"%addr,35,16)
        oled.text("天气:%s"%weather,35,32)
        oled.text("温度:%s℃"%temperature,35,48)
        oled.show()
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 📋Shell调试窗口信息
      在这里插入图片描述
    • 本示例参考文章:https://mc.dfrobot.com.cn/thread-26740-1-1.html
  • 相关阅读:
    【算法入门与九月打卡】—— 第五天
    Git常用命令
    《网络安全笔记》第十四章:交换机的工作原理
    Java基于SSM的海淘商城系统
    华为机试 - 区间交集
    3D手眼标定之原理(3D Vision Roboot Eye-to-hand Calibration)
    Java学习笔记3.9.1 Lambda表达式 - Lambda表达式入门
    Codeforces Round #574 (Div. 2) C. Basketball Exercise
    PLC面向对象编程系列之如何设计分解状态机(FSM)的状态
    实验三 循环结构程序设计(Python)
  • 原文地址:https://blog.csdn.net/weixin_42880082/article/details/126561392