2017年8月12日 星期六

MicroPython on ESP8266 (十八) : SSD1306 液晶顯示器測試

做完 MicroPython 的 1602 LCD 顯示器實驗後, 我又在零件箱裡找到之前買的 NOKIA 5110 LCD 顯示器與 0.91 吋 128*32 解析度的 SSD1306 OLED 顯示器, 原本是想測試 NOKIA 5110, 但是一直不得法, 所以就把目標瞄向 SSD1306 顯示器了.

本系列之前的測試紀錄參考 :

MicroPython on ESP8266 (二) : 數值型別測試
MicroPython on ESP8266 (三) : 序列型別測試
MicroPython on ESP8266 (四) : 字典與集合型別測試
MicroPython on ESP8266 (五) : WiFi 連線與 WebREPL 測試
MicroPython on ESP8266 (六) : 檔案系統測試
MicroPython on ESP8266 (七) : 時間日期測試
MicroPython on ESP8266 (八) : GPIO 測試
MicroPython on ESP8266 (九) : PIR 紅外線移動偵測
MicroPython v1.9.1 版韌體測試
MicroPython on ESP8266 (十) : socket 模組測試
MicroPython on ESP8266 (十一) : urllib.urequest 模組測試
MicroPython on ESP8266 (十二) : urequests 模組測試
MicroPython on ESP8266 (十三) : DHT11 溫溼度感測器測試
MicroPython 使用 ampy 突然無法上傳檔案問題
MicroPython on ESP8266 (十四) : 網頁伺服器測試
WeMOS D1 Mini 開發板測試
MicroPython on ESP8266 (十五) : 光敏電阻與 ADC 測試
MicroPython on ESP8266 (十六) : 蜂鳴器測試
# MicroPython on ESP8266 (十七) : 液晶顯示器 1602A 測試

MicroPython 文件參考 :

MicroPython tutorial for ESP8266  (官方教學)
http://docs.micropython.org/en/latest/micropython-esp8266.pdf
http://docs.micropython.org/en/latest/pyboard/library/usocket.html#class-socket
http://docs.micropython.org/en/v1.8.7/esp8266/library/usocket.html#module-usocket
https://gist.github.com/xyb/9a4c8d7fba92e6e3761a (驅動程式)

SSD1306 是中國顯示晶片設計商晶門 (Solomon System, 港交所 2878) 為小型 OLED/PLED 共陰極顯示器所設計的單晶片 CMOS 128*64 點矩陣式驅動 IC, 具有 256 階對比控制, 128*64 bits SRAM 顯示記憶體以及內建震盪器, 大幅簡化了外部電路與降低了功率消耗, 最大電流僅 15mA, 參考 :

http://www.solomon-systech.com/zh/product/display-ic/oled-driver-controller/ssd1306/

其規格書可在 Adafruit 網站下載 :

https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf

我手上這塊模組的面板非常小巧, 只有 0.91 吋, 解析度 128*32, 為 I2C 介面只有 VCC (3.3V), GND, 以及 SCL 與 SDA 四支腳, 方便插在麵包板上做實驗. 硬體接線非常簡單. 當時在露天買花了 90 元, 但現在似乎漲到 100 元 :

向 tsai_pl 與 bluetaipei 購買零組件兩批
0.91吋 OLED 128x32 Arduino SSD1306 3.3V $100

在 AliExpree 這塊模組最低賣 US$2.68  (一件免運費), 折合台幣 80 元 :

# Free Shipping 0.91 Inch 128x32 IIC I2C White OLED LCD Display DIY Oled Module SSD1306 Driver IC DC 3.3V 5V For Arduino PIC US$2.68

淘寶網賣人民幣 8.8 元, 折合台幣 41 元, 但運費選擇淘寶集運每公斤 ¥14 人民幣起跳, 合計 ¥22.8 元合台幣 108 元, 要買兩件以上才會跟 AliExpress 或露天打平.

0.91寸OLED液晶模塊 IIC接口 128*32點陣 SSD1306驅動  ¥ 8.80

買 5 個是 ¥44 元, 大陸國內段運費 ¥10 合計 ¥54 元, 折合台幣約 246 元, 台灣集運運費不計的話平均每個 49 元. 若選擇淘寶 4PX 集運 1KG 內 ¥14 元, 合計是 ¥54+¥14=¥68 元, 合台幣 324 元, 平均每個 65 元, 也比露天便宜. 如果買 10 個含運費是 ¥88+10+14=¥112, 合台幣 533 元, 平均每個 53 元, 即買越多就能把運費攤平了.

另外還有使用 128*64 解析度面板 (與 SSD1306 控制能力一致) 的模組, 大小跟 NOKIA 5110 LCD 模組相同 :

OLED液晶模組 0.96吋 黃藍雙色 12864點陣 SSD1306驅動 3.3V-5V 4線IIC介面 $200

SSD1306 的 MicroPython 驅動程式可從下面兩個 GitHub 下載, 此驅動模組同時支援 I2C 與 SPI 介面, 只要傳入 I2C 或 SPI 物件即可驅動顯示器 :

micropython/drivers/display/ssd1306.py  (MicroPython 官網)

此驅動模組提供下列 SSD1306_I2C 物件函數以方便操控面板的顯示. API 整理如下 :

 SSD1306_I2C 物件的方法 說明
 fill(col) 將顯示記憶體全部畫素填入 col=1 (亮) 或 0 (暗)
 pixel(x, y, col) 在顯示記憶體指定畫素位置 (x, y) 填入 col=1 (亮) 或 0 (暗)
 text(string, x, y, col=1) 在顯示記憶體指定畫素位置 (x, y) 起填入預設 col=1 之字串 string
 show() 將顯示記憶體內容輸出於面板顯示內容
 scroll(dx, dy) 將顯示記憶體畫素內容向上下 (dy) 或向左右 (dx) 捲動

注意, 顯示面板座標 (x,y) 的原點位置 (0, 0) 是在 0.91 吋模組 SDA 腳的那一側. 將驅動模組 ssd1306.py 上傳到 ESP8266 後, 即可參考 Adafruit 網站的用法說明進行測試 :

How to use a SSD1306 OLED display with MicroPython boards

不過這篇文章使用的是 Adafruit 的 Circuit Python 控制板搭配其 FeatherWing OLED SSD1306 模組, 面板一樣是 128x32 OLED, 但模組價格卻非常貴, 要價 US$14.95, 合新台幣約 449 元, 是我買的模組的 4 倍多, 只是搭配 Adafruit 的 ESP8266 Feather 系列開發板較方便而已, 可以上下疊起來 :

FeatherWing OLED - 128x32 OLED Add-on For All Feather Boards US$14.95

Adafruit CircuitPython+FeatherWing OLED 所使用的 SSD1306 驅動模組事實上是從上面 MicroPython 官網版本修改而來, 僅加入適應 CircuitPython 部分 :

https://github.com/adafruit/Adafruit_CircuitPython_SSD1306

在下面的測試中我仍然使用 WeMOS D1 Mini 微控器, 當然 ESP-01 模組, ESP-12 模組或 NodeMCU 開發板也是可以的. SSD1360 模組與 D1 Mini 的硬體接線很簡單, 就是 SCL 接 D1 (GPIO 5), SDA 接 D2 (GPIO 4), 如下圖所示 :


軟體部分, ESP8266 模組需燒錄 MicroPython v1.8.5 版以上韌體, 否則無法執行 ssd1306.py 模組. 首先須匯入已上傳的 ssd1306.py 模組, 另外還須從 machine 模組匯入 Pin 與 I2C :

import ssd1306
from machine import Pin, I2C
i2c=I2C(scl=Pin(5), sda=Pin(4))                   #指定 GPIO 腳建立 I2C 物件
oled=ssd1306.SSD1306_I2C(128, 32, i2c)   #指定解析度建立 SSD1306 物件

此處 SSD1306_I2C() 建構函數的傳入參數為 (width, height, i2c), 分別是 OLED 面板的寬度 128, 高度 32, 以及 I2C 物件, 傳回值為一個 SSD1306_I2C 物件, 這樣便可以呼叫 SSD1306_I2C 物件的方法如 fill(), pixel() 或 text() 等來顯示資訊.

例如 fill(1) 是將顯示記憶體的每一個像素都點亮, 用 show() 輸出後就會使整個螢幕全亮; 而 fill(0) 則是全暗, 相當於 1602 LCD 裡的 clear() 清除螢幕的作用.

oled.fill(1)      #將顯示記憶體每一個畫素填滿 1 (點亮)
oled.show()    #將顯示記憶體內容輸出到面板 (全亮)
oled.fill(0)      #將顯示記憶體每一個畫素填滿 0 (熄滅)
oled.show()    #將顯示記憶體內容輸出到面板 (全暗)


事實上 fill(1) 與 fill(0) 與下列用 pixel() 填像素作用相同 :

    for x in range(128):
        for y in range(32):
            oled.pixel(x, y, 1)

    for x in range(128):
        for y in range(32):
            oled.pixel(x, y, 0)

注意, 不管是 fill(), pixel() 或 text(), 都只是在 SSD1306 內的顯示記憶體上操作而已, 並不是實際輸出到顯示面板上, 執行後面板上不會顯示任何訊息, 必須呼叫 show() 函數將顯示記憶體之內容輸出才會顯示.

測試 1 : 每隔 2 秒點亮與熄滅整個 OLED 面板

#main.py
import ssd1306
from machine import Pin, I2C
import time

i2c=I2C(scl=Pin(5), sda=Pin(4))                
oled=ssd1306.SSD1306_I2C(128, 32, i2c)

while True:
    oled.fill(1)        
    oled.show()  
    time.sleep(2)
    oled.fill(0)      
    oled.show()
    time.sleep(2)  


接下來要測試 pixel(x, y, col) 方法, 此方法是在指定的畫素位置 (x, y) 輸出一個亮點 (col=1) 或暗點 (col=0). 下列測試 2 是要在面板的四周畫出一個長方形框  :

測試 2 : 在 OLED 面板的四周畫出一個長方形框 

#main.py
import ssd1306
from machine import Pin, I2C

i2c=I2C(scl=Pin(5), sda=Pin(4))                
oled=ssd1306.SSD1306_I2C(128, 32, i2c)

for i in range(128):
        oled.pixel(i, 0, 1)             #填上邊框
        oled.pixel(i, 31, 1)           #填下邊框
        if i < 32:
            oled.pixel(0, i, 1)          #填左邊框
            oled.pixel(127, i, 1)      #填右邊框
oled.show()

此程式為了節省時間只使用一個迴圈填畫素, 由於 X 軸座標 0~127, Y 軸座標 0~31, range() 函數會傳回串列 [0,1,....,127], 以 X 軸座標當迴圈運行 128 次, 同時填上邊框與下邊框, 在 i 於 0~31 期間, 也同時填 Y 軸的左右邊框. 結果如下 :


下面測試 3 要測試 text() 方法, 即在 OLED 面板上顯示文字. 此驅動程式的 text() 以 8*8=64 bits 描繪一個 ASCII 碼, 不過實際描繪寬度為 6 bits, 亦即左右各保留 1 px 的間距以免前後字元連在一起. 例如下面是 '0' 的 Bitmap :


因此一列可顯示 128/8=16 個字元, 一行可顯示 32/8=4 列字元, 整個面板可顯示 16*4=64 個字元. 在顯示多列文字時, 下一列 y 座標應往下移 8 才不會黏在一起, 如下面測試 3 所示 :

測試 3 : 在 OLED 面板上顯示文字 

 #main.py
import ssd1306
from machine import Pin, I2C

i2c=I2C(scl=Pin(5), sda=Pin(4))                
oled=ssd1306.SSD1306_I2C(128, 32, i2c)

str='012345678901234567890'

for i in range(5):
   oled.text(str, 0, i*8)
oled.show()


此程式中 str 含有 20 個字元, 卻只顯示 16 個, 可見多出來的字元事實上被驅動模組丟棄了, 並沒有存入顯示記憶體中.

接下來參考 MicroPython 1602 測試的範例 4, 在 OLED 面板上顯示溫溼度與亮度等氣候資訊, 硬體接線參考 :

MicroPython on ESP8266 (十七) : 液晶顯示器 1602A 測試

跟 1602 LCD 顯示器一樣, SSD1306 OLED 也是一列只能顯示 16 個字元, 但可以顯示 4 列, 所以我把顯示格式做了些調整, 第一列顯示日期與星期, 第二列顯示時分秒, 第三列顯示環境資訊, 規劃如下 :


不過 SSD1306_I2C 物件的 text() 方法只要指定第一個字元要顯示的開始座標位置即可, 其他字元會自動往後一一顯示, 超過 16 個的字元會被丟棄.

測試 4 : 在 OLED 面板上顯示溫溼度亮度資訊

#main.py
import time, ntptime, dht
from machine import I2C, Pin, ADC
import ssd1306

def fill_zero(n):
    if n<10:
        return '0' + str(n)
    else:
        return str(n)

def fill_blank(n):      
    if n<10:
        return ' ' + str(n)
    else:
        return str(n)

i2c=I2C(scl=Pin(5), sda=Pin(4))                
oled=ssd1306.SSD1306_I2C(128, 32, i2c)

DHTPIN=Pin(16, Pin.IN)
LEDPIN=Pin(2, Pin.OUT)
d=dht.DHT11(DHTPIN)
adc=ADC(0)
week={0:'Mon',1:'Tue',2:'Wed',3:'Thu',4:'Fri',5:'Sat',6:'Sun'}

try:
    ntptime.settime()
except:
    pass

n=0
while True:
    d.measure()            
    t=d.temperature()
    h=d.humidity()
    a=adc.read()
    a=int(0.3*a + 0.7*adc.read())
    a=round(a*100/1024)

    utc_epoch=time.mktime(time.localtime())
    Y,M,D,H,m,S,W,DY=time.localtime(utc_epoch + 28800)
    YMD='%s-%s-%s' % (str(Y),fill_zero(M),fill_zero(D))
    WD=week[W]
    HmS='%s:%s:%s' % (fill_zero(H),fill_zero(m),fill_zero(S))
    A='%s%%' % (fill_blank(a))
    T='%sC' % (fill_blank(t))
    H='%sH' % (fill_blank(h))

    line1='%s %s' % (YMD, WD)
    line2=HmS
    line3='%s %s %s' % (T, H, A)
    oled.fill(0)                    #清除螢幕以免舊資訊殘留疊加
    #oled.show()                 #此指令會造成頁面跳動
    oled.text(line1, 0, 0)     #顯示日期星期
    oled.text(line2, 0, 8)     #顯示時間
    oled.text(line3, 0, 16)   #顯示溫溼度亮度
    oled.show()

    time.sleep(1)
    n=n+1
    if n >= 3600:
        try:
            ntptime.settime()
        except:
            pass
        n=0


因為要避免舊資訊殘留疊加, 所以在更新顯示資料前先做清除螢幕動作, 但是這麼一來卻造成換頁時螢幕會跳動一下. 我嘗試用 pixel() 方法只清除要顯示的前三列, 即將上述程式碼中的 oled.fill(0) 改成下列 :

    for x in range(128):
        for y in range(24):
            oled.pixel(x, y, 0)

但這並無法消除換頁跳動現象, 還是跟 fill(0) 一樣.  畢竟 SSD1306 與 1602 不同, 1602 的 putstr() 方法是以字元為單位, 更新時會清除舊的 bitmap 以新的字元之 bitmap 取代, 所以 1602 不需要清除畫面, 也就不會有換頁跳動現象.

2017-08-12 補充 :

晚上回到鄉下後測試發現, 只要單用 oled.fill(0) 即可清除舊畫面, 亦即只要將顯示記憶體全部 reset 就達到清除畫面目的了, 不需要呼叫 oled.show() 去刷新頁面, 就是後面這指令造成換頁跳動現象的.

在上面測試 4 裡我用 'C' 代表攝氏度數的單位, 能不能像 1602 測試中那用 custom_char() 方法自訂右上角一個小圈圈的度數符號呢? 很可惜地, 在 ssd1306.py 模組中並未提供類似的函數, 因此必須自己做, 攝氏度數小圓圈的 bitmap 如下 :

依據上面對於顯示區域的規劃, 此符號之座標位置為 (2,2), 即原本顯示 'C' 之處. 描繪此符號需使用 pixel() 方法將指定之畫素點亮, 上面這個度數小圓圈的亮點畫素座標可用下列元組串列表示, 只要將這 8 個點設為 1 即可 :

bitlist=[(2,0),(3,0),(1,1),(4,1),(1,2),(4,2),(2,3),(3,3)]

這些亮點的實際位置可用字元座標計算而得, 因為每個字元是 8*8 bitmap 所描繪, 因此畫素實際座標就是字元座標乘以 8 再加上字元內亮點座標即得. 例如第一個亮點之字元內座標為 (2,0)經此換算為實際座標 (18, 16), 其中 18=2*8+2, 16=2*8+0.

我模仿 1602 驅動模組的 custom_char() 函數寫了一個 SSD1306 版的函數如下 :

def custom_char(oled, x, y, bitlist, col=1):
   for i in bitlist:
       oled.pixel(x*8+i[0], y*8+i[1], col)

呼叫此函數後自訂字元的 bitmap 亮點就被寫入指定位置的顯示記憶體了. 完整程式如下, 新增的程式碼以藍色標示 :

測試 5 : 在 OLED 面板上顯示溫溼度亮度資訊 (使用自訂之攝氏度數符號)

#main.py
import time, ntptime, dht
from machine import I2C, Pin, ADC
import ssd1306

def fill_zero(n):
    if n<10:
        return '0' + str(n)
    else:
        return str(n)

def fill_blank(n):    
    if n<10:
        return ' ' + str(n)
    else:
        return str(n)

def custom_char(oled, x, y, bitlist, col=1):
   for i in bitlist:  
       oled.pixel(x*8+i[0], y*8+i[1], col)  

i2c=I2C(scl=Pin(5), sda=Pin(4))              
oled=ssd1306.SSD1306_I2C(128, 32, i2c)

DHTPIN=Pin(16, Pin.IN)
LEDPIN=Pin(2, Pin.OUT)
d=dht.DHT11(DHTPIN)
adc=ADC(0)
week={0:'Mon',1:'Tue',2:'Wed',3:'Thu',4:'Fri',5:'Sat',6:'Sun'}

try:
    ntptime.settime()
except:
    pass

n=0
while True:
    d.measure()          
    t=d.temperature()
    h=d.humidity()
    a=adc.read()
    a=int(0.3*a + 0.7*adc.read())
    a=round(a*100/1024)

    utc_epoch=time.mktime(time.localtime())
    Y,M,D,H,m,S,W,DY=time.localtime(utc_epoch + 28800)
    YMD='%s-%s-%s' % (str(Y),fill_zero(M),fill_zero(D))
    WD=week[W]
    HmS='%s:%s:%s' % (fill_zero(H),fill_zero(m),fill_zero(S))
    A='%s%%' % (fill_blank(a))
    T='%s' % (fill_blank(t))
    H='%sH' % (fill_blank(h))

    line1='%s %s' % (YMD, WD)
    line2=HmS
    line3='%s %s %s' % (T, H, A)
    oled.fill(0)  
    oled.text(line1, 0, 0)  
    oled.text(line2, 0, 8)  
    oled.text(line3, 0, 16)
    bitlist=[(2,0),(3,0),(1,1),(4,1),(1,2),(4,2),(2,3),(3,3)]  
    custom_char(oled, 2, 2, bitlist)  
    oled.show()

    time.sleep(1)
    n=n+1
    if n >= 3600:
        try:
            ntptime.settime()
        except:
            pass
        n=0



其實我們可以在每一個字元 8*8=64 bits 畫板上製作任何 bitmap 呼叫 text() 方法顯示於 16*4=64 個字元位置上, 在下列測試 6 中我製作了 0%, 20%, 40%, 60%, 80%, 100% 電池電量顯示符號 (包括垂直與水平方向) :

測試 6 : 在 OLED 面板上顯示 WiFi 連線狀態

#main.py
import time
from machine import I2C, Pin
import ssd1306

def custom_char(oled, x, y, bitlist, col=1):
   for i in bitlist:
       oled.pixel(x*8+i[0], y*8+i[1], col)

i2c=I2C(scl=Pin(5), sda=Pin(4))              
oled=ssd1306.SSD1306_I2C(128, 32, i2c)

bv0p=[(3,0),(4,0),(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),
      (1,2),(6,2),(1,3),(6,3),(1,4),(6,4),(1,5),(6,5),
      (1,6),(6,6),(1,7),(2,7),(3,7),(4,7),(5,7),(6,7)]
bv100p=[(3,0),(4,0),(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),
        (2,2),(3,2),(4,2),(5,2),
        (2,3),(3,3),(4,3),(5,3),
        (2,4),(3,4),(4,4),(5,4),
        (2,5),(3,5),(4,5),(5,5),
        (2,6),(3,6),(4,6),(5,6),
        (1,2),(6,2),(1,3),(6,3),(1,4),(6,4),(1,5),(6,5),
        (1,6),(6,6),(1,7),(2,7),(3,7),(4,7),(5,7),(6,7)]
bv20p=[(3,0),(4,0),(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),
       (2,6),(3,6),(4,6),(5,6),
       (1,2),(6,2),(1,3),(6,3),(1,4),(6,4),(1,5),(6,5),
       (1,6),(6,6),(1,7),(2,7),(3,7),(4,7),(5,7),(6,7)]
bv40p=[(3,0),(4,0),(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),
       (2,5),(3,5),(4,5),(5,5),
       (2,6),(3,6),(4,6),(5,6),
       (1,2),(6,2),(1,3),(6,3),(1,4),(6,4),(1,5),(6,5),
       (1,6),(6,6),(1,7),(2,7),(3,7),(4,7),(5,7),(6,7)]
bv60p=[(3,0),(4,0),(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),
       (2,4),(3,4),(4,4),(5,4),
       (2,5),(3,5),(4,5),(5,5),
       (2,6),(3,6),(4,6),(5,6),
       (1,2),(6,2),(1,3),(6,3),(1,4),(6,4),(1,5),(6,5),
       (1,6),(6,6),(1,7),(2,7),(3,7),(4,7),(5,7),(6,7)]
bv80p=[(3,0),(4,0),(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),
       (2,3),(3,3),(4,3),(5,3),
       (2,4),(3,4),(4,4),(5,4),
       (2,5),(3,5),(4,5),(5,5),
       (2,6),(3,6),(4,6),(5,6),
       (1,2),(6,2),(1,3),(6,3),(1,4),(6,4),(1,5),(6,5),
       (1,6),(6,6),(1,7),(2,7),(3,7),(4,7),(5,7),(6,7)]
bh100p=[      (1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),
              (1,2),(2,2),(3,2),(4,2),(5,2),(6,2),(7,2),
        (0,3),(1,3),(2,3),(3,3),(4,3),(5,3),(6,3),(7,3),
        (0,4),(1,4),(2,4),(3,4),(4,4),(5,4),(6,4),(7,4),
              (1,5),(2,5),(3,5),(4,5),(5,5),(6,5),(7,5),
              (1,6),(2,6),(3,6),(4,6),(5,6),(6,6),(7,6)]                
bh80p=[       (1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),
              (1,2),      (3,2),(4,2),(5,2),(6,2),(7,2),
        (0,3),(1,3),      (3,3),(4,3),(5,3),(6,3),(7,3),
        (0,4),(1,4),      (3,4),(4,4),(5,4),(6,4),(7,4),
              (1,5),      (3,5),(4,5),(5,5),(6,5),(7,5),
              (1,6),(2,6),(3,6),(4,6),(5,6),(6,6),(7,6)]
bh60p=[      (1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),
             (1,2),            (4,2),(5,2),(6,2),(7,2),
       (0,3),(1,3),            (4,3),(5,3),(6,3),(7,3),
       (0,4),(1,4),            (4,4),(5,4),(6,4),(7,4),
             (1,5),            (4,5),(5,5),(6,5),(7,5),
             (1,6),(2,6),(3,6),(4,6),(5,6),(6,6),(7,6)]
bh40p=[      (1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),
             (1,2),                  (5,2),(6,2),(7,2),
       (0,3),(1,3),                  (5,3),(6,3),(7,3),
       (0,4),(1,4),                  (5,4),(6,4),(7,4),
             (1,5),                  (5,5),(6,5),(7,5),
             (1,6),(2,6),(3,6),(4,6),(5,6),(6,6),(7,6)]
bh20p=[      (1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),
             (1,2),                        (6,2),(7,2),
       (0,3),(1,3),                        (6,3),(7,3),
       (0,4),(1,4),                        (6,4),(7,4),
             (1,5),                        (6,5),(7,5),
             (1,6),(2,6),(3,6),(4,6),(5,6),(6,6),(7,6)]

bh0p=[       (1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),
             (1,2),                              (7,2),
       (0,3),(1,3),                              (7,3),
       (0,4),(1,4),                              (7,4),
             (1,5),                              (7,5),
             (1,6),(2,6),(3,6),(4,6),(5,6),(6,6),(7,6)]  
custom_char(oled, 0, 0, bv0p)
custom_char(oled, 1, 0, bv20p)
custom_char(oled, 2, 0, bv40p)
custom_char(oled, 3, 0, bv60p)
custom_char(oled, 4, 0, bv80p)
custom_char(oled, 5, 0, bv100p)
custom_char(oled, 0, 1, bh0p)
custom_char(oled, 1, 1, bh20p)
custom_char(oled, 2, 1, bh40p)
custom_char(oled, 3, 1, bh60p)
custom_char(oled, 4, 1, bh80p)
custom_char(oled, 5, 1, bh100p)  
oled.show()



最後我想修改設定無線網路連線的程式, 讓 ESP8266 模組連線 AP 時的狀態可以在 SSD1306 OLED 面板上, 參考 :

MicroPython v1.9.1 版韌體測試

測試 7 : 在 OLED 面板上顯示 WiFi 連線狀態

#main.py
from machine import Pin,PWM,I2C
import network
import time
import ssd1306  

WAIT_FOR_CONNECT=8

pwm2=PWM(Pin(2), freq=5, duty=512)

i2c=I2C(scl=Pin(5), sda=Pin(4))
oled=ssd1306.SSD1306_I2C(128, 32, i2c)  

def set_ap():
    html="""
    <!DOCTYPE html>
    <html>
      <head><title>AP Setup</title></head>
      <body>
        %s
      </body>
    </html>
    """
    form="""
        <form method=get action='/update_ap'>
          <table border="0">
            <tr>
              <td>SSID</td>
              <td><input name=ssid type=text></td>
            </tr>
            <tr>
              <td>PWD </td>
              <td><input name=pwd type=text></td>
            </tr>
            <tr>
              <td></td>
              <td align=right><input type=submit value=Connect></td>
            </tr>
          </table>
        </form>
    """
    import socket
    addr=socket.getaddrinfo('192.168.4.1', 80)[0][-1]
    s=socket.socket()
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(addr)
    s.listen(5)
    print('listening on', addr)
    while True:
        cs, addr=s.accept()
        print('client connected from', addr)
        data=cs.recv(1024)      
        request=str(data,'utf8')
        print(request, end='\n')
        if request.find('update_ap?') == 5:
            para=request[request.find('ssid='):request.find(' HTTP/')]
            ssid=para.split('&')[0].split('=')[1]
            pwd=para.split('&')[1].split('=')[1]
            sta.connect(ssid,pwd)
            while not sta.isconnected():
                pass
            print('Connected:IP=',sta.ifconfig()[0])
            cs.send(html % 'Connected:IP=' + sta.ifconfig()[0])
            oled.fill(0)
            oled.text('IP=' + sta.ifconfig()[0],0,0)
            oled.show()
        else:
            cs.send(html % form)
        cs.close()
    s.close()

def get_ip():
  return (network.WLAN(network.STA_IF).ifconfig()[0],
  network.WLAN(network.AP_IF).ifconfig()[0])

def ap_on():
  network.WLAN(network.AP_IF).active(True)

def ap_off():
  network.WLAN(network.AP_IF).active(False)

#try connecting to lastest configured AP
sta=network.WLAN(network.STA_IF)
sta.active(True)
print('Connecting to AP ...')
oled.text('Connecting to AP',0,0)
oled.show()  
time.sleep(WAIT_FOR_CONNECT)
if not sta.isconnected():
    set_ap()
else:
    pwm2.deinit()
    Pin(2).value(0)
    print('Connected:IP=', sta.ifconfig()[0])
    oled.fill(0)
    oled.text('IP=' + sta.ifconfig()[0],0,0)
    oled.show()
    #Application code is written here or import from a separate file
    import myapp
    myapp.main()

藍色部分為新增的程式碼, 當 ESP-12 模組或 D1 Mini 開機時  OLED 面板會顯示 'Connecting to AP', 板上 LED 會快閃, 若能連線到之前設定的 AP, 則 8 秒後會顯示 'ap=192.168.xxx.xxx', 板上 LED 熄滅, 並開始執行 myapp.py 應用程式. 若無法連線前次 AP, 可用手機 WiFi 連線 SSID 為 MicroPython_xxxxxx 之 ESP8266 內建基地台 (密碼為 micropythoN), 開啟手機瀏覽器連線 192.168.4.1, 在顯示的網頁中輸入附近可連線之 AP, 然後將 ESP-12 或 D1 Mini 重開機即可.

參考 :

# DIY - ESP8266:SSD1306 0.96吋 I2C OLED 屏電路(十九)
I2C driver for micropython esp8266 ssd1306 OLED (ssd1306a.py is paired down and works with the font.py file to allowing generating text)
# ssd1306 using I2C on the esp8266
# https://github.com/adafruit/micropython-adafruit-ssd1306
# ESP01(ESP8266) driving an SSD1306 display in microPython
A playground for various MicroPython scripts 
# A sensor monitor with OLED in MicroPython
ssd1306 using I2C on the esp8266
I2C driver for micropython esp8266 ssd1306 OLED (另一個驅動程式)
Micropython - OLED module with the ssd1306 chip (Pyboard)
Adafruit's Extended Ascii font as stmhal/framebuf's default font. #2673

沒有留言 :