2017年8月17日 星期四

組裝可調式直流電源供應器 (二)

上周六收到露天賣家寄來的變壓器, 因為要回鄉下所以就擱著. 周一晚上取出測量電壓, 功能均正常, 其二次端有七條線, 共三組輸出 : 白色是 4V 輸出, 綠色是 28.5V 輸出, 而黃色有兩條對黑線都是 11.4 輸出 :

白色 4V

綠色 28.5V

黃色 11.4V

週二下班去禾樺買直流輸出用的博士端子, 1 公尺長電源線, 黑色塑膠外殼, 以及 7P 公電源連接線花了 152 元, 加上小北買的電源插頭含線 79 元, 組這個電源供應器已經花了 200+80+152+79=511 元.

週二晚上吃過飯就進行組裝, 同時在輸出端加焊 LED 電壓計, 測試正常可調 0~30V 輸出, 但因電壓計最低工作電壓 4.5V, 所以低於此電壓就無法顯示 :


接下來就是要在外殼上鑽洞把電路板與變壓器鎖在機箱內, 先用油性筆訂出鑽孔位置, 等本周回鄉下再用鑽孔機施工.

2017年8月16日 星期三

Goodfello, Bengio, Courville 的深度學習電子書

今天在 GitHub 上找到 Goodfello, Bengio, 與 Courville 合寫的深度學習免費電子書 "Deep Learning", 此書屬於理論書籍, 從線性代數, 機率統計與資訊理論帶入機器學習與深度學習等 AI 主題的理論介紹, PDF 檔可在 GitHub 下載 :

https://github.com/HFTrader/DeepLearningBook

此書作者 Goodfello 目前任職 Google, 是 Google Brain 成員之一, 而 Bengio 與 Courville 則是加拿大蒙特婁大學教授, 與此書相關之習題, 教學投影片可在下列網站下載 :

www.deeplearningbook.org

參考 :

‘Deep Learning’ by Goodfellow, Bengio, and Courville


2017年8月13日 星期日

2017 年第 32 周記事

週六晚上我在改程式時, 姊姊走過來說, 她發現這個禮拜是開學前能回鄉下的最後一周, 也是, 下周六要去海港聚餐慶祝姊姊考上大學與父親節, 接下來兩周要去動漫展擺攤, 然後就要準備開學了. 以後就只有一個月回來一次, 想到離巢期開始襲來, 心裡有點恐慌. 好快啊! 18 年就在不經意中溜走了, 想想初為人父, 半夜起來兩三次餵奶似乎還是昨日之事, 一回神姐姐已經要上大學了. 這些日子到底是怎麼溜走的呢?

早上經過市圖進去撈到五本書 :

  1. Arduino穿戴式裝置專案製作
  2. 8051語音互動專題製作與應用
  3. 電路板設計快速上手 :從EAGLETM開始學設計原理到電路板實作
  4. C基礎講座
  5. C語言程式設計入門與實作 
  6. C語言程式設計與應用
三本 C 語言的書是要教二哥 C 語言的陣列, 函數, 指標這三個最重要單元用的.

今日中午正昏昏欲睡進入夢鄉未久就被外面車子聲吵醒, 原來是阿泉伯的兒子帶他朋友來幫我家芒果接枝, 其實我覺得我家的海頓就很好吃了, 不管是愛文, 玉文等比不上. 下午帶了水果跟姊姊去文昌宮還願, 雖然凡事得靠自己努力, 但機運卻是努力不來的.

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 on ESP8266 (十四) : 網頁伺服器測試
WeMOS D1 Mini 開發板測試
MicroPython on ESP8266 (十五) : 光敏電阻與 ADC 測試
MicroPython on ESP8266 (十六) : 蜂鳴器測試
# MicroPython on ESP8266 (十七) : 液晶顯示器 1602A 測試

MicroPython 文件參考 :

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

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/

其規格書如下 :

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 (另一個驅動程式)

2017年8月11日 星期五

鄉村基礎木作第五課 : 鎖抽屜前板與製作門板

因本周六木樂園公休, 所以昨晚吃過飯後就去上木工第五課. 接續上週進度, 這回是給上週做好的兩個抽屜裝上前板. 由於抽屜箱底下會裝上 5mm 厚的軌道與前檔條, 所以前板下源須往下突出此厚度, 左右對稱後用 F 形夾固定住, 不用上膠 (因有四個螺絲可以鎖很緊),  準備上螺絲 :


因為前板下方的螺絲太裡面, 電鑽須加一個如下圖所示的 90 度轉向頭才好鎖上去 :


到這裡抽屜算是完成了. 接下來是製作門板框, 由厚1.8 cm 寬 6 cm 的長短各一對板材拼合而成, 為了使結構強固, 接合處須鑽洞後以木釘膠合, 所以老師要我先畫線定出要鑽孔的中心線. 先在桌上擺好門框, 將紋路較佳的朝外在四塊板材上用鉛筆做上記號表示此為正面 (這很重要, 否則到後面會搞混), 然後將要接合的板材對齊夾緊即可劃中心線. 厚 1.8cm 故中心點是 0.9cm, 而寬度 6cm 左右 3cm 故從左右各量 1.5cm 劃出中心點 :


畫好後用尖起子在中心點刺出一個小洞, 這樣鑽洞時才不會歪掉. 接下來要調整鑽頭, 下鑽深度必須比木釘的一半再多 1mm, 否則太淺會無法讓板材密合. 橫方向鑽洞時直接將板材放在鑽台上調好高度對準中心點即可鑽洞, 但縱方向就沒辦法了, 須將鑽台鬆開轉到後面, 然後把板材用固定底座夾緊後放在鑽床底盤上才能鑽 :


鑽好洞後先塞木釘把門板組合起來, 看看有否密合, 若接合處高低不平, 可用手持電鑽在反方向將洞加大一些微調. 然後就可以上膠塞木釘組合, 趁膠尚未凝固用夾子上下前後夾緊使接合處密合, 靜置一天後再取下 :


門框這樣就先打住, 等膠乾了後铣好溝再裝中間的門板. 剩下一點時間老師教我用線鋸機鋸底板下方左右兩個裝飾用的造型板. 先用模板在板材上畫線後手持線鋸機沿線裁切, 但這線鋸機有點重量又不好控制, 我一連鋸了三塊還是不甚合格. 最後老師說如果後面沒有鋸出更好的, 就用今天較好的兩個來修.

補記 : 購買姊姊的筆電

姊姊要去台北上大學了, 需要一台電腦, 所以 8/8 放榜當晚就帶她去自由路的燦坤看一下, 對, 原本只是要看一下而已, 然後再研究看看, 結果卻當場決定買了 ACER 這台 VX5-591G-54N2, 因為是父親節特價 31900, 只到 8/8, 所以查了一下網路最低也要 33000, 所以就決定買了.

今天把這台電腦帶去公司開會用, 順便安裝工具軟體, 然後製作映像檔, Win 10 是在控制台/檔案歷程記錄裡面點 "系統映像備份/建立系統映像", 然後會掃描可用來存放映像檔的儲存媒體, 必須備份到不同硬碟才行 (例如外接 USB 硬碟), 參考 :

# 升級 Win10 並建立系統映像檔

2017年8月10日 星期四

C 語言自學手冊

二哥下學期要升上高二了, 新學期的導師是數學老師, 對 Coding 也有涉獵, 在他原先帶的高一班上有組織一個資訊小組學習 C 語言, 高二選組後也有部分同學來到二哥這班, 導師又繼續糾合對寫程式有興趣者加入, 二哥說他也加入了. 最近老師介紹這本 "C 語言自學手冊" 的教材給他們 :

https://hackersir.gitbooks.io/c/content/

我稍微看了一下覺得很不錯, 他說可否印出來方便閱讀, 所以昨天下班前找出高師大附近以前幫我印碩士論文的盧老闆電話, 詢問 192 頁含膠裝要多少錢, 呵呵, 只要 130 元, 叫我現場等一下馬上就可取貨. 盧老闆多年不見還是一樣穿西裝皮鞋上班, 真是非常紳士的人.