姊姊要去台北上大學了, 需要一台電腦, 所以 8/8 放榜當晚就帶她去自由路的燦坤看一下, 對, 原本只是要看一下而已, 然後再研究看看, 結果卻當場決定買了 ACER 這台 VX5-591G-54N2, 因為是父親節特價 31900, 只到 8/8, 所以查了一下網路最低也要 33000, 所以就決定買了.
今天把這台電腦帶去公司開會用, 順便安裝工具軟體, 然後製作映像檔, Win 10 是在控制台/檔案歷程記錄裡面點 "系統映像備份/建立系統映像", 然後會掃描可用來存放映像檔的儲存媒體, 必須備份到不同硬碟才行 (例如外接 USB 硬碟), 參考 :
# 升級 Win10 並建立系統映像檔
2017年8月11日 星期五
2017年8月10日 星期四
C 語言自學手冊
二哥下學期要升上高二了, 新學期的導師是數學老師, 對 Coding 也有涉獵, 在他原先帶的高一班上有組織一個資訊小組學習 C 語言, 高二選組後也有部分同學來到二哥這班, 導師又繼續糾合對寫程式有興趣者加入, 二哥說他也加入了. 最近老師介紹這本 "C 語言自學手冊" 的教材給他們 :
# https://hackersir.gitbooks.io/c/content/
我稍微看了一下覺得很不錯, 他說可否印出來方便閱讀, 所以昨天下班前找出高師大附近以前幫我印碩士論文的盧老闆電話, 詢問 192 頁含膠裝要多少錢, 呵呵, 只要 130 元, 叫我現場等一下馬上就可取貨. 盧老闆多年不見還是一樣穿西裝皮鞋上班, 真是非常紳士的人.
# https://hackersir.gitbooks.io/c/content/
我稍微看了一下覺得很不錯, 他說可否印出來方便閱讀, 所以昨天下班前找出高師大附近以前幫我印碩士論文的盧老闆電話, 詢問 192 頁含膠裝要多少錢, 呵呵, 只要 130 元, 叫我現場等一下馬上就可取貨. 盧老闆多年不見還是一樣穿西裝皮鞋上班, 真是非常紳士的人.
2017年8月9日 星期三
向露天 56jkx6p 購買方形變壓器等零件一批
上週組裝的直流電源供應器少了一個 28V 變壓器, 擬向露天賣家 56jkx6p 購買, 為攤分運費同時添購需要用的一些零組件如下 (LM358N 是實驗常用的比較器 IC, DIP 封裝) :
# 方型變壓器(3.9V-0V)(28V-0V)(11V-11V-0V)DB519 $80
# IC LM358N / LM393N /HA17358/C741C/TI9240 $3*10=30
# SM2P對插線LED電源 天花板LED燈(線徑1.7mm 公母2條1組) $9*2=18
# CH2端子台按壓式 電源線快速接頭 快速接線端子(1包10個) $15*2=30
合計 80 + 30 + 18 + 30 =158 + 超商取貨付款 60 共 218 元
# 方型變壓器(3.9V-0V)(28V-0V)(11V-11V-0V)DB519 $80
# IC LM358N / LM393N /HA17358/C741C/TI9240 $3*10=30
# SM2P對插線LED電源 天花板LED燈(線徑1.7mm 公母2條1組) $9*2=18
# CH2端子台按壓式 電源線快速接頭 快速接線端子(1包10個) $15*2=30
合計 80 + 30 + 18 + 30 =158 + 超商取貨付款 60 共 218 元
2017年8月8日 星期二
姊姊考上台師大
今天早上接到姐姐電話, 說考上台師大了, 聽得出來非常高興, 因為這是她填的第一志願, 而且指考名額只有 5 名. 唐朝詩人孟郊在中進士後所做的詩 "登科後" 云 : "春風得意馬蹄疾, 一日看盡長安花", 這應該是姐姐今日心情的寫照吧! 我中午跟爸說了這件事, 晚上打電話時他說高興得午覺睡不著哩.
雖然花了快半年準備指考比較辛苦, 但考上自己夢想的科系也是值得的. 當初繁星她列為第一志願的高師大視設這回卻在選填時排到第 6 了. 查榜發現第二, 三志願的台藝大動畫, 視設依去年錄取成績可上, 但今年分數比較高, 姊姊竟然沒上哩! 所以即使將台藝大列第一第二, 台師大列第三的話, 還是會錄取台師大. 她說國中最要好的同班同學阿媛也是上台師大 (繁星), 這樣去台北讀書也有個伴.
雖然花了快半年準備指考比較辛苦, 但考上自己夢想的科系也是值得的. 當初繁星她列為第一志願的高師大視設這回卻在選填時排到第 6 了. 查榜發現第二, 三志願的台藝大動畫, 視設依去年錄取成績可上, 但今年分數比較高, 姊姊竟然沒上哩! 所以即使將台藝大列第一第二, 台師大列第三的話, 還是會錄取台師大. 她說國中最要好的同班同學阿媛也是上台師大 (繁星), 這樣去台北讀書也有個伴.
2017年8月7日 星期一
MicroPython on ESP8266 (十七) : 液晶顯示器 1602A 測試
這幾天在零件箱裡找到之前購買的兩個 1602 LCD 顯示器, 一個是 5V 的, 另一個是 3.3V 的, 同時還發現剛好有買過兩個 1602 I2C 介面模組, 與 1602 搭配可以減少 GPIO 的耗用, 因為直接控制 1602 需要用掉 7 個 GPIO, 而使用 I2C 則只需兩個即可 (連接 I2C 設備的 SDA 與 SCL), 可節省 5 個 IO 腳. 由於 1602 I2C 模組買來時已經焊好排針 (1*16), 所以我跑去禾樺買了兩個 1*16 的排母焊在 1602 背面, 這樣 1602 I2C 模組就可以插在背面的排母了, 不用焊死, 如下圖所示 :
注意, 上面那塊是 3.3V 的 1602, 下面那塊則是 5V 的, 差別是 3.3V 的在背面左邊圈起來處有多一顆 IC 與 2 顆電容, 至於 1602 I2C 模組則不論 3.3V 或 5V 均可以運作. 因此上面那塊 1602 I2C 模組的 VCC 就接 3.3V; 而下面這塊的 VCC 則接 5V. 我有試過將 5V 版的 1602 + I2C 接 3.3V 電源還是可用, 但是要將對比調到最大才看得清楚顯示的字.
事實上這個 1602 I2C 模組是利用一顆 PCF8574 IC 來將兩個 GPIO 腳 (即 SCL 與 SDA) 擴展為 8 個 IO 來控制 1602, 這樣就大幅減少微控器 GPIO 腳的耗用, 此外還可以利用板上的 A0, A1, A3 端子 (未焊), 多工選取多個 PCF8574, 最多可接 8 個 PCF8574. 此模組一端有兩支腳用來控制是否要連接背光電源, 預設用跳帽連起來表示有接上背光電源. 另一端是接微控器的 VCC, GND, SCL, 與 SDA 的 I2C 介面. 板上的藍色可變電阻是用來調整背光對比的, 順時針旋轉增加對比, 逆時針是減少對比. 注意, 3.3V 版的 1602 對比不可調太弱, 否則調節背光的 IC 會很燙, 參考 :
# 【DIY_LAB#790】Arduino IIC/I2C介面LCD1602/2004轉接板 $30
# 【傑森創工】藍底 1602 LCD 顯示器 已焊2004轉接板 $95
# Arduino IO擴展模組 PCF8574T模組 電子積木 $105
# PCF8574 IIC LCD1602 轉接板 轉接模組 W70[276507-045] $52
1602 I2C 模組上的 PCF8574 晶片有兩種, PCF8574T 的預設位址是 0x27; 而 PCF8574AT 則是 0x3F, 寫程式時需注意 1602 I2C 模組所使用的晶片是 PCF8574T 還是 PCF8574AT, 參考下面這篇的說明 :
# 【盼盼22】 LCD 1602 I2C 轉接板 1602液晶顯示器 IIC 界面接口 $29
關於 Arduino 版 1602 操作參考下列這篇, 不過我那時不是使用 I2C, 而是直接使用 Arduino 的 DIO 腳去控制1602, 本篇則是要探索在 ESP8266 上如何用 MicroPython 控制 1602 的顯示 :
# Arduino 液晶顯示器測試
本系列之前的測試紀錄參考 :
# 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 文件參考 :
# 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
所謂的 I2C 其實是 IIC (Inter-Integrated Circuit) 通訊協定的另一種寫法, I2C 被設計用來在系統內各晶片之間交換資料, 例如 CPU 從顯示晶片取得解析度等參數即透過 I2C 協定; 物聯網感測模組如陀螺儀, 加速度計, 顯示器, 或 RTC 等模組等也大都使用 I2C 介面通訊, I2C 協定較少用在設備間通訊.
I2C 是一個兩線的的串列通訊協定 (2-wire serial protocol), 只需要 SCL (Serial Clock Line) 與 SDA (Serial Data Line) 兩條線即可通訊, 不過與 RS-232 串列通訊不同之處是兩端不需要設定相同的傳輸速率, 因為 I2C 是透過 SCL 這條線來協調通訊速率的, 通常使用標準速率 100KHz 傳輸, 快速模式下可達 400KHz.
MicroPython on ESP8266 實作之 I2C 函式庫參見 :
# class I2C – a two-wire serial protocol
# i2c-bus
下面用來控制 1602 液晶顯示器的 MicroPython 函式庫主要是參考下面這篇文章 :
# LCD 1602 - Library
其中 mcauser 先生的回覆內容有具體的做法指引 :
# https://forum.micropython.org/viewtopic.php?t=2858#p16925
以下實驗我使用 WeMOS D1 Mini, 其 D1 腳 (GPIO 5) 即用作 I2C 的 SCL 線, 而 D2 腳 (GPIO 4) 即為 SDA 線, 接線如下 :
因我使用 I2C 模組控制 1602 液晶模組, 所以需要下面這兩個檔案 :
for I2C backpack:
https://github.com/dhylands/python_lcd/ ... i2c_lcd.py
https://github.com/dhylands/python_lcd/ ... cd_test.py
其實應該到這兩個檔案的上層 GitHub 去下載全部檔案比較完整 :
# https://github.com/dhylands/python_lcd
點右邊的 "Clone or download" 鈕可下載專案的全部檔案 zip 檔 :
解壓縮後從 python_lcd-master\lcd 目錄下複製 lcd_api.py 與 esp8266_i2c_lcd.py 這兩個 1602 LCD 函式庫檔案到我們的測試目錄 D:\ESP8266\test 下, 準備上傳 ESP8266.
其中 lcd_api.py 是 MicroPython 與 1602 顯示晶片 HD44780 溝通的 API, 而 esp8266_i2c_lcd.py 則是在此 API 基礎上實作了 i2clcd 物件的函式庫以方便操控 1602, 其完整物件方法寫在 lcd_api.py 檔案中, 整理如下 :
這裡面值得一提的是 custom_char() 方法, 此方法可讓我們儲存自定義字元於 CGRAM 中, 其位址為 0x0 至 0x7 共 8 個 byte 可用. 1602 液晶顯示器是以日立 HD44780 液晶顯示晶片為控制器, 裡面包含了 CGROM (儲存 ASCII 碼的 bitmap), DDRAM (80 Bytes 顯示記憶體, 儲存欲顯示的字元), 以及我們可以控制的 CGRAM (8 Bytes 擴展字元記憶體, 儲存自定義字元的 bitmap), 關於 1602 的內部記憶體參考 :
# 【51单片机】1602 CGRAM、CGROM及DDRAM的作用
# 新手必看1602字符液晶显示原理+实例详解
# 1602 字符液晶使用说明 (pdf)
總之, 透過上面這些方法就可以很方便地操控 1602 了.
欲操控 1602 首先必須從 machine 模組匯入 I2C 與 Pin 函數, 並從 sp8266_i2c_lcd 模組匯入 I2cLcd 函數 :
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
這樣便能呼叫 I2C() 建構式指定 I2C 通訊所使用的 GPIO 腳與速率來建立 I2C 物件 :
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)
此指令指定 GPIO 5 為 SCL 線, GPIO 4 為 SDA 線, 通訊速率為 400KHz 的 I2C 匯流排. 關於 I2C 物件參考 MicroPython 官方教學文件 :
# http://docs.micropython.org/en/v1.8.7/esp8266/esp8266/quickref.html#i2c-bus
# class I2C – a two-wire serial protocol (for WIPY)
接下來不需要利用 I2C 物件的讀寫指令存取 I2C 匯流排, 而是呼叫 esp8266_i2c_lcd.py 模組中的 I2cLcd() 建構式以建立 I2cLcd 物件, 這樣就能利用上述的 I2cLcd 物件方法來操控 1602 了, 不須直接處理 I2C Bus 上低階的 raw data, 這就是 API 包裝的好處 :
lcd=I2cLcd(i2c, 0x27, 2, 16)
其 API 為 :
I2cLcd(i2c, i2c_addr, num_lines, num_columns)
此處四個參數 :
i2c : I2C 物件
i2c addr : PCF8574 IO 擴展晶片位址 (預設是 0x27, 即使用 PCF8574T 晶片)
num lines : LCD 列數
num_columns : LCD 欄數
因我使用的是 1602, 所以列數為 2, 欄數為 16. 這樣便建立了一個位址為 0x27 的 I2C Slave 設備, 即 1602 I2C 模組, 注意, 若 1602 I2C 模組使用的是 PCF8574AT 晶片, YK6
則位址必須改為 0x3F.
這時若呼叫 I2C 的 scan() 方法掃描 I2C 匯流排上的設備將回傳 10 進位的設備位址串列, 因目前只有 0x27 位址, 10 進位是 39, 因此回傳 [39] :
>>> i2c.scan()
[39]
建立了一個 I2cLcd 物件後可以呼叫上表中的方法操控 1602 了.
測試 1 : 在 1602 上顯示 Hello World
#main.py
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000) #指定 I2C 介面之 GPIO 與傳輸速率
lcd=I2cLcd(i2c, 0x27, 2, 16) #指定 I2C Slave 設備位址與顯示器之列數, 行數
lcd.putstr("Hello World!\nIt's working!") #顯示字串
lcd.move_to(0,1) #移到游標至第二列第一行位置 (跳行)
lcd.putstr("It's working!") #顯示字串
上面的程式中 lcd.putstr("Hello World!") 執行後, 游標會停在 "!" 後面, 須先用 move_to() 將游標移到座標 (0,1), 即第二列第一行, 再顯示第二列字串, 若沒有用 move_to(), 就會在目前游標位置 ("!" 後面) 繼續輸出, 結果如下 :
其實跳行動作不需要用到 move_to(0, 1), 直接使用跳脫字元 "\n" 也有跳行效果, 因此上述程式也可改為如下 :
#main.py
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000) #指定 I2C 介面之 GPIO 與傳輸速率
lcd=I2cLcd(i2c, 0x27, 2, 16) #指定 I2C Slave 設備位址與顯示器之列數, 行數
lcd.putstr("Hello World!\nIt's working!") #顯示字串 (使用 "\n" 跳行)
接下來我想連線 NTP 伺服器, 取得目前的網路時間顯示在液晶螢幕上, 這就必須匯入 MicroPython 內建的 ntptime 與 time 模組, 參考 :
# MicroPython on ESP8266 (七) : 時間日期測試
測試 2 : 從 NTP 伺服器取得 UTC 時間轉成台灣時間顯示於 1602 上
#main.py
import time, ntptime
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
def fill_zero(n):
if n<10:
return '0' + str(n)
else:
return str(n)
<10: p="">
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
ntptime.settime() #從 NTP 伺服器取得 UTC 時間後設定本地時鐘
while True:
utc_epoch=time.mktime(time.localtime()) #將本地時鐘轉成 UTC 之時戳
Y,M,D,H,m,S,ms,W=time.localtime(utc_epoch + 28800) #UTC 加 8 小時=台灣時間
YMD='%s-%s-%s' % (str(Y),fill_zero(M),fill_zero(D)) #日期字串
HmS='%s:%s:%s' % (fill_zero(H),fill_zero(m),fill_zero(S)) #時間字串
lcd.move_to(0, 0) #移動游標到第一列第一行
lcd.putstr(YMD) #顯示日期
lcd.move_to(0, 1) #移動游標到第二列第一行
lcd.putstr(HmS) #顯示時間
time.sleep(1) #暫停 1 秒
此程式一開始執行時便利用 ntptime.settime() 透過網路從 NTP 伺服器取得 GMT 時戳 (單位:毫秒) , 並用此時戳來設定 ESP8266 內部時鐘, 接著就跳進無限迴圈每秒自內建時鐘讀取此時戳, 加上 28800 毫秒即得台灣時間時戳 (因台灣為 GMT + 8 小時, 8*60*60=28800), 然後用自訂函數 fill_zero() 將日期時間格式標準化, 即個位數字前面補 0, 再分別將日期與時間顯示於 1602 的第一列與第二列 :
10:>
從影片中可知在 38:00 時出現跳秒現象 (一次跳兩秒), 直接從 38:00 跳躍到 38:02. 雖然每秒去讀取時戳, 但由於運算需要些微時間, 累積起來就會造成秒差, 觀察大約每 10 秒就會出現跳秒現象.
注意, 上面這個測試是假定 ESP8266 已可連線到無線基地台上網際網路, ESP8266 會記住最近一次的 SSID 與 PWD, 一開機即可自動連線, 所以上面程式可直接存成 main.py 上傳 ESP8266. 如果需要修改連線之基地台, 例如移動到另一個環境無法連線最近一次設定之基地台, 可將上面程式存成 myapp.py, 然後參考下列這篇裡面的 main.py, 拿掉最後兩列前面的 # :
# WeMOS D1 Mini 開發板測試
#main.py
import time
WAIT_FOR_CONNECT=8
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])
else:
cs.send(html % form)
cs.close()
s.close()
import network
sta=network.WLAN(network.STA_IF)
sta.active(True)
print('Connecting to AP ...')
time.sleep(WAIT_FOR_CONNECT)
if not sta.isconnected():
set_ap()
else:
print('Connected:IP=', sta.ifconfig()[0])
#Application code is written here or import from a separate file
import myapp
myapp.main()
將此 main.py 與測試 2 的 myapp.py 上傳 ESP8266, 然後打開手機 WiFi 連線 ESP8266 內建 AP, 其 SSID 為 "MicroPython-" 開頭 (後面是 MAC 後 6 碼), 預設密碼 "micropythoN". 接著用手機瀏覽器 (建議用 Chrome) 連線網址 192.168.4.1 即可設定欲連線之無線基地台了.
D:\ESP8266\test>ampy --port COM8 put main.py
D:\ESP8266\test>ampy --port COM8 put myapp.py
REPL 輸出如下 :
PYB: soft reboot
#14 ets_task(40100164, 3, 3fff829c, 4)
WebREPL is not configured, run 'import webrepl_setup'
Connecting to AP ...
Connected:IP= 192.168.43.227
(2017, 8, 7, 1, 16, 32, 0, 219)
從上面這個因誤差造成跳秒問題延伸, 上述程式只在程式一開始與 NTP 伺服器對時 (更新內部時鐘), 然後就自己跑自己的, 這樣時間久了之後也會因 ESP8266 震盪晶體準確度問題而與 NTP 時鐘產生誤差, 因此比較好的做法是固定周期 (例如每小時) 再與 NTP 伺服器對時一次更新 ESP8266 內部時鐘, 如下列測試 3 :
測試 3 : 每小時與 NTP 伺服器對時一次顯示台灣時間顯示於 1602 上
import time,ntptime
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
def fill_zero(n):
if n<10:
return '0' + str(n)
else:
return str(n)
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
ntptime.settime() #與 NTP 伺服器初次對時
n=0 #設定重新對時計數器初始值
while True:
utc_epoch=time.mktime(time.localtime())
Y,M,D,H,m,S,ms,W=time.localtime(utc_epoch + 28800)
YMD='%s-%s-%s' % (str(Y),fill_zero(M),fill_zero(D))
HmS='%s:%s:%s' % (fill_zero(H),fill_zero(m),fill_zero(S))
lcd.move_to(0, 0)
lcd.putstr(YMD)
lcd.move_to(0, 1)
lcd.putstr(HmS)
time.sleep(1)
n=n+1 #計數器增量
if n >= 3600: #超過 1 小時 (3600 秒)
ntptime.settime() #重新對時 1 次
n=0 #計數器歸零重新計數
測試 4 : 顯示日期時間, 溫溼度, 亮度於 1602 液晶顯示器
接下來要測試 1602 HD44780 內 CGRAM 的自定義記憶體的用法, 希望能將上圖中的攝氏溫度單位由 C 改為右上角一個小圈圈的度數符號, ASCII 中沒有此符號, 必須利用 CGRAM 的 custom_char() 功能自製字元的 Bitmap.
自訂字元用法參考上面 Github 下載檔案 i2c_lcd_test.py 裡面的範例, 是透過呼叫 I2cLcd 物件的 custom_char() 方法將 8 個 bytes 的二進位陣列寫入 0~7 的 CGRAM 位址中, 然後用 Python 內建函數 chr() 傳入 0~7 位址可提取此自訂字元.
# https://github.com/dhylands/python_lcd
1602 LCD 的每一個字元解析度是 5*8=40 px (不過最後一列似乎沒有使用), 我製作了兩種攝氏度數符號的 bitmap 如下, 一個靠左, 一個靠右 :
其儲存指令分別為 :
lcd.custom_char(0, bytearray([0x07,0x05,0x07,0x00,0x00,0x00,0x00,0x00])) #靠右
lcd.custom_char(0, bytearray([0x1C,0x14,0x1C,0x00,0x00,0x00,0x00,0x00])) #靠左
在下面測試 5 程式中, 我在 CGRAM 位址 0 加入了此攝氏度數符號字元, 修改了變數 T 的輸出格式用此自訂字元替換測試 4 中的 'C' 字元 :
測試 5 : 顯示日期時間, 溫溼度, 亮度於 1602 液晶顯示器 (使用自定義字元)
#myapp.py
import time, ntptime, dht
from machine import I2C, Pin, ADC
from esp8266_i2c_lcd import I2cLcd
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
lcd.custom_char(0, bytearray([0x1C,0x14,0x1C,0x00,0x00,0x00,0x00,0x00]))
DHTPIN=Pin(16, Pin.IN)
LEDPIN=Pin(14, Pin.OUT)
d=dht.DHT11(DHTPIN)
adc=ADC(0)
week={0:'Mo',1:'Tu',2:'We',3:'Th',4:'Fr',5:'Sa',6:'Su'}
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)
YMDW='%s-%s-%s%s' % (str(Y),fill_zero(M),fill_zero(D),week[W])
HmS='%s:%s:%s' % (fill_zero(H),fill_zero(m),fill_zero(S))
A='%s%%' % (fill_blank(a))
T='%s%s' % (fill_blank(t),chr(0))
H='%sH' % (fill_blank(h))
lcd.move_to(0, 0)
lcd.putstr(YMDW)
lcd.move_to(13, 0)
lcd.putstr(A)
lcd.move_to(0, 1)
lcd.putstr(HmS)
lcd.move_to(9, 1)
lcd.putstr(T)
lcd.move_to(13, 1)
lcd.putstr(H)
time.sleep(1)
n=n+1
if n >= 3600:
try:
ntptime.settime()
except:
pass
n=0
測試結果顯示, 似乎靠左跟度數近一點較好. 另外, 上面的程式碼中的 ntptime.settime() 改用 try except 包起來, 因為若網路中斷時呼叫 settime() 會出現例外 (OSError: -2) 而使程式當掉停止執行, 必須做例外處理, 當網路無法存取時就跳過 :
PYB: sof#9 ets_task(40100164, 3, 3fff829c, 4)
WebREPL is not configured, run 'import webrepl_setup'
Connecting to AP ...
Connected:IP= 192.168.43.227
Traceback (most recent call last):
File "main.py", line 86, in <module>
File "myapp.py", line 13, in <module>
File "ntptime.py", line 30, in settime
File "ntptime.py", line 18, in time
OSError: -2
觀察發現程式碼多了後, 跳秒情形似乎更明顯了, 大概每 4~6 秒就會跳一格. 其實秒數並不是很重要, 既然很難避免跳秒, 不如乾脆去除秒數, 只要顯示到分就好, 這樣就可以把時分與年月日這兩個時間相關訊息一起放在第一列剛好塞滿, 亮度就移到第二列顯示. 少了秒數後第二列就有點空, 所以我把星期簡碼由兩個字元改為三個字元, 星期字典也修改為 :
week={0:'Mon',1:'Tue',2:'Wed',3:'Thu',4:'Fri',5:'Sat',6:'Sun'}
LCD 顯示區域重新規劃如下 :
我將測試 5 程式修改如下, 修改部分以藍色標示 :
import time, ntptime, dht
from machine import I2C, Pin, ADC
from esp8266_i2c_lcd import I2cLcd
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),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
lcd.custom_char(0, bytearray([0x1C,0x14,0x1C,0x00,0x00,0x00,0x00,0x00]))
DHTPIN=Pin(16, Pin.IN)
LEDPIN=Pin(14, 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))
Hm='%s:%s' % (fill_zero(H),fill_zero(m))
A='%s%%' % (fill_blank(a))
T='%s%s' % (fill_blank(t),chr(0))
H='%sH' % (fill_blank(h))
lcd.move_to(0, 0)
lcd.putstr(YMD)
lcd.move_to(11, 0)
lcd.putstr(Hm)
lcd.move_to(0, 1)
lcd.putstr(week[W])
lcd.move_to(5, 1)
lcd.putstr(T)
lcd.move_to(9, 1)
lcd.putstr(H)
lcd.move_to(13, 1)
lcd.putstr(A)
time.sleep(1)
n=n+1
if n >= 3600:
try:
ntptime.settime()
except:
pass
n=0
呵呵, 這個配置看起來比較好看, 溫溼度亮度等環境資訊都在第二列, 而且我們通常只要知道幾點幾分就夠了, 秒數並不是很重要.
接下來要測試背光函數 backlight_on() 與 backlight_off(). 開啟背光功能大約會增加 20mA 左右的耗電, 基於省電考量, 我希望只有在偵測到附近有人經過時才開啟背光, 否則就關閉以節省用電, 這就需要一個 PIR 紅外線感測器, 關於 PIR 用法參考 :
# Arduino 測試 : PIR 紅外線移動偵測 (一)
# Arduino 測試 : PIR 紅外線移動偵測 (二)
# MicroPython on ESP8266 (八) : GPIO 測試
# MicroPython on ESP8266 (九) : PIR 紅外線移動偵測
注意, PIR 感測器的 VCC 上面是標 +5V, 雖然接 D1 Mini 的 3.3V 接腳似乎也可以運作, 但是感覺較易受雜訊影響, 建議接在 D1 Mini 的 5V 接腳較穩.
我將上面測試 6 程式加上了 PIR 感測器程式碼如下, 修改與增加部分以藍色標示 :
測試 7 : 顯示日期時間, 溫溼度, 亮度於 1602 液晶顯示器 (利用 PIR 紅外線控制背光)
#myapp.py
import time, ntptime, dht
from machine import I2C, Pin, ADC
from esp8266_i2c_lcd import I2cLcd
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 int0(p):
global backlight_state
global triggerCount
global triggerLimit
backlight_state=1
if triggerCount < triggerLimit:
triggerCount=triggerCount + 2
PIRPIN=Pin(0, Pin.IN)
PIRPIN.irq(trigger=Pin.IRQ_RISING, handler=int0)
backlight_state=0
triggerCount=0
triggerLimit=5
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
lcd.custom_char(0, bytearray([0x1C,0x14,0x1C,0x00,0x00,0x00,0x00,0x00]))
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))
Hm='%s:%s' % (fill_zero(H),fill_zero(m))
A='%s%%' % (fill_blank(a))
T='%s%s' % (fill_blank(t),chr(0))
H='%sH' % (fill_blank(h))
lcd.move_to(0, 0)
lcd.putstr(YMD)
lcd.move_to(11, 0)
lcd.putstr(Hm)
lcd.move_to(0, 1)
lcd.putstr(week[W])
lcd.move_to(5, 1)
lcd.putstr(T)
lcd.move_to(9, 1)
lcd.putstr(H)
lcd.move_to(13, 1)
lcd.putstr(A)
if backlight_state==1:
lcd.backlight_on()
LEDPIN.value(0)
triggerCount=triggerCount - 1
if triggerCount <= 0:
backlight_state=0
else:
lcd.backlight_off()
LEDPIN.value(1)
print("IRQ Trigger Count:",triggerCount,'Backlight State:',backlight_state)
time.sleep(1)
n=n+1
if n >= 3600:
try:
ntptime.settime()
except:
pass
n=0
此程式使用 GPIO 0 (D1 Mini 的 D4 腳) 來接 PIR 感測器的輸出, 當 PIR 偵測到人體紅外線時會輸出一個 HIGH 位準, 持續約 0.5 秒左右, 可透過 PIR 背板上的一顆可變電阻調整 HIGH 持續時間, 但最長持續時間也是很有限 (約 3~5 秒), 使得 LCD 上的資訊還沒看仔細, 背光就暗掉了.
為了在持續移動觸發 (閉鎖時間 2.5 秒) 下讓 LCD 背光維持在亮燈狀態, 我參考之前在 PIR 感應照明控制測試中的作法, 利用 PIR 輸出的上升緣 (LOW -> HIGH) 觸發外部硬體中斷 (IRQ) 控制三個全域變數 backlight_state, triggerCount, 以及 triggerLimit 之值, 以便在連續移動觸發時能使背光持續點亮.
不過此處為了不影響每個迴圈大致維持每秒跑一圈, 我沒有使用 time.sleep() 來延長背光持續點亮時間, 而是每次 PIR 觸發時讓 triggerCount 增量 2 而非 1, 這樣雖然每次迴圈會讓 triggerCount 減量 1, 但因增量速度是減量速度的兩倍以上, 所以在持續觸發情況下 triggerCount 不會很快歸零導致背光忽明忽亮, 這樣就能維持背光在點亮狀態. 而 triggerLimit 的作用則是讓持續觸發有個天花板擋著, 讓加倍增量的 triggerCount 能夠收斂, 如同核反應堆中阻止中子數增生的碳棒一樣.
參考 :
# Python based library for talking to character based LCDs.
# Arduino 使用 1602 IIC(I2C) LCD 點陣液晶模組
# I2C Bus 簡介
# I2C Bus 與 SMBUS 有甚麼不同
# MicroPython Library for I2C 2x16 LCD Screens (for WiPy/LoPy/SiPy only)
# Bucknalla/micropython-i2c-lcd (for WiPy/LoPy/SiPy only)
# https://github.com/Bucknalla
# dhylands/python_lcd
2017-08-10 補充 :
由於本實驗使用 WeMOS D1 mini, 其 A0 (ADC) 接腳接受的電壓是 0~3.3V (與 ESP-12 模組的 0~1V 不同), 對應到 AD 轉換後的 0~1023, 所以此處的亮度偵測使用一個 CdS 光敏電阻與 10K 歐姆電阻分壓, A0 (ADC) 直接讀取 10K 歐姆電阻的壓降後取百分數, 其值 (0~100) 即與光度成正比了, 參考下面這篇的補充說明 :
# MicroPython on ESP8266 (十五) : 光敏電阻與 ADC 測試
注意, 上面那塊是 3.3V 的 1602, 下面那塊則是 5V 的, 差別是 3.3V 的在背面左邊圈起來處有多一顆 IC 與 2 顆電容, 至於 1602 I2C 模組則不論 3.3V 或 5V 均可以運作. 因此上面那塊 1602 I2C 模組的 VCC 就接 3.3V; 而下面這塊的 VCC 則接 5V. 我有試過將 5V 版的 1602 + I2C 接 3.3V 電源還是可用, 但是要將對比調到最大才看得清楚顯示的字.
事實上這個 1602 I2C 模組是利用一顆 PCF8574 IC 來將兩個 GPIO 腳 (即 SCL 與 SDA) 擴展為 8 個 IO 來控制 1602, 這樣就大幅減少微控器 GPIO 腳的耗用, 此外還可以利用板上的 A0, A1, A3 端子 (未焊), 多工選取多個 PCF8574, 最多可接 8 個 PCF8574. 此模組一端有兩支腳用來控制是否要連接背光電源, 預設用跳帽連起來表示有接上背光電源. 另一端是接微控器的 VCC, GND, SCL, 與 SDA 的 I2C 介面. 板上的藍色可變電阻是用來調整背光對比的, 順時針旋轉增加對比, 逆時針是減少對比. 注意, 3.3V 版的 1602 對比不可調太弱, 否則調節背光的 IC 會很燙, 參考 :
# 【DIY_LAB#790】Arduino IIC/I2C介面LCD1602/2004轉接板 $30
# 【傑森創工】藍底 1602 LCD 顯示器 已焊2004轉接板 $95
# Arduino IO擴展模組 PCF8574T模組 電子積木 $105
# PCF8574 IIC LCD1602 轉接板 轉接模組 W70[276507-045] $52
1602 I2C 模組上的 PCF8574 晶片有兩種, PCF8574T 的預設位址是 0x27; 而 PCF8574AT 則是 0x3F, 寫程式時需注意 1602 I2C 模組所使用的晶片是 PCF8574T 還是 PCF8574AT, 參考下面這篇的說明 :
# 【盼盼22】 LCD 1602 I2C 轉接板 1602液晶顯示器 IIC 界面接口 $29
關於 Arduino 版 1602 操作參考下列這篇, 不過我那時不是使用 I2C, 而是直接使用 Arduino 的 DIO 腳去控制1602, 本篇則是要探索在 ESP8266 上如何用 MicroPython 控制 1602 的顯示 :
# Arduino 液晶顯示器測試
本系列之前的測試紀錄參考 :
# 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 文件參考 :
# 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
I2C 是一個兩線的的串列通訊協定 (2-wire serial protocol), 只需要 SCL (Serial Clock Line) 與 SDA (Serial Data Line) 兩條線即可通訊, 不過與 RS-232 串列通訊不同之處是兩端不需要設定相同的傳輸速率, 因為 I2C 是透過 SCL 這條線來協調通訊速率的, 通常使用標準速率 100KHz 傳輸, 快速模式下可達 400KHz.
MicroPython on ESP8266 實作之 I2C 函式庫參見 :
# class I2C – a two-wire serial protocol
# i2c-bus
下面用來控制 1602 液晶顯示器的 MicroPython 函式庫主要是參考下面這篇文章 :
# LCD 1602 - Library
其中 mcauser 先生的回覆內容有具體的做法指引 :
# https://forum.micropython.org/viewtopic.php?t=2858#p16925
以下實驗我使用 WeMOS D1 Mini, 其 D1 腳 (GPIO 5) 即用作 I2C 的 SCL 線, 而 D2 腳 (GPIO 4) 即為 SDA 線, 接線如下 :
因我使用 I2C 模組控制 1602 液晶模組, 所以需要下面這兩個檔案 :
for I2C backpack:
https://github.com/dhylands/python_lcd/ ... i2c_lcd.py
https://github.com/dhylands/python_lcd/ ... cd_test.py
其實應該到這兩個檔案的上層 GitHub 去下載全部檔案比較完整 :
# https://github.com/dhylands/python_lcd
點右邊的 "Clone or download" 鈕可下載專案的全部檔案 zip 檔 :
解壓縮後從 python_lcd-master\lcd 目錄下複製 lcd_api.py 與 esp8266_i2c_lcd.py 這兩個 1602 LCD 函式庫檔案到我們的測試目錄 D:\ESP8266\test 下, 準備上傳 ESP8266.
其中 lcd_api.py 是 MicroPython 與 1602 顯示晶片 HD44780 溝通的 API, 而 esp8266_i2c_lcd.py 則是在此 API 基礎上實作了 i2clcd 物件的函式庫以方便操控 1602, 其完整物件方法寫在 lcd_api.py 檔案中, 整理如下 :
| I2cLcd 物件的方法 | 說明 |
| move_to(x, y) | 移動游標至座標 (x, y) |
| putchar(str) | 從游標目前位置開始顯示字串 str, 然後將游標移到字串結束之下一位置 |
| putchar(char) | 從游標目前位置開始顯示字元 char, 然後將游標移到下一位置 |
| custom_char(location, charmap) | 將字元圖樣 charmap 寫入 CGRAM 記憶體位址 location=0~7 |
| clear() | 清除 LCD 螢幕並將游標移至左上角座標 (0, 0) |
| show_cursor() | 顯示游標 (底線) |
| hide_cursor() | 隱藏游標 |
| blink_cursor_on() | 開啟游標閃爍功能 |
| blink_cursor_off() | 關閉游標閃爍功能 |
| display_on() | 開啟顯示功能 |
| display_off() | 關閉顯示功能 (即顯示白屏) |
| backlight_on() | 開啟背光 |
| backlight_off() | 關閉背光 |
這裡面值得一提的是 custom_char() 方法, 此方法可讓我們儲存自定義字元於 CGRAM 中, 其位址為 0x0 至 0x7 共 8 個 byte 可用. 1602 液晶顯示器是以日立 HD44780 液晶顯示晶片為控制器, 裡面包含了 CGROM (儲存 ASCII 碼的 bitmap), DDRAM (80 Bytes 顯示記憶體, 儲存欲顯示的字元), 以及我們可以控制的 CGRAM (8 Bytes 擴展字元記憶體, 儲存自定義字元的 bitmap), 關於 1602 的內部記憶體參考 :
# 【51单片机】1602 CGRAM、CGROM及DDRAM的作用
# 新手必看1602字符液晶显示原理+实例详解
# 1602 字符液晶使用说明 (pdf)
總之, 透過上面這些方法就可以很方便地操控 1602 了.
欲操控 1602 首先必須從 machine 模組匯入 I2C 與 Pin 函數, 並從 sp8266_i2c_lcd 模組匯入 I2cLcd 函數 :
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
這樣便能呼叫 I2C() 建構式指定 I2C 通訊所使用的 GPIO 腳與速率來建立 I2C 物件 :
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)
此指令指定 GPIO 5 為 SCL 線, GPIO 4 為 SDA 線, 通訊速率為 400KHz 的 I2C 匯流排. 關於 I2C 物件參考 MicroPython 官方教學文件 :
# http://docs.micropython.org/en/v1.8.7/esp8266/esp8266/quickref.html#i2c-bus
# class I2C – a two-wire serial protocol (for WIPY)
接下來不需要利用 I2C 物件的讀寫指令存取 I2C 匯流排, 而是呼叫 esp8266_i2c_lcd.py 模組中的 I2cLcd() 建構式以建立 I2cLcd 物件, 這樣就能利用上述的 I2cLcd 物件方法來操控 1602 了, 不須直接處理 I2C Bus 上低階的 raw data, 這就是 API 包裝的好處 :
lcd=I2cLcd(i2c, 0x27, 2, 16)
I2cLcd(i2c, i2c_addr, num_lines, num_columns)
此處四個參數 :
i2c : I2C 物件
i2c addr : PCF8574 IO 擴展晶片位址 (預設是 0x27, 即使用 PCF8574T 晶片)
num lines : LCD 列數
num_columns : LCD 欄數
因我使用的是 1602, 所以列數為 2, 欄數為 16. 這樣便建立了一個位址為 0x27 的 I2C Slave 設備, 即 1602 I2C 模組, 注意, 若 1602 I2C 模組使用的是 PCF8574AT 晶片, YK6
則位址必須改為 0x3F.
這時若呼叫 I2C 的 scan() 方法掃描 I2C 匯流排上的設備將回傳 10 進位的設備位址串列, 因目前只有 0x27 位址, 10 進位是 39, 因此回傳 [39] :
>>> i2c.scan()
[39]
建立了一個 I2cLcd 物件後可以呼叫上表中的方法操控 1602 了.
測試 1 : 在 1602 上顯示 Hello World
#main.py
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000) #指定 I2C 介面之 GPIO 與傳輸速率
lcd=I2cLcd(i2c, 0x27, 2, 16) #指定 I2C Slave 設備位址與顯示器之列數, 行數
lcd.putstr("Hello World!\nIt's working!") #顯示字串
lcd.move_to(0,1) #移到游標至第二列第一行位置 (跳行)
lcd.putstr("It's working!") #顯示字串
上面的程式中 lcd.putstr("Hello World!") 執行後, 游標會停在 "!" 後面, 須先用 move_to() 將游標移到座標 (0,1), 即第二列第一行, 再顯示第二列字串, 若沒有用 move_to(), 就會在目前游標位置 ("!" 後面) 繼續輸出, 結果如下 :
其實跳行動作不需要用到 move_to(0, 1), 直接使用跳脫字元 "\n" 也有跳行效果, 因此上述程式也可改為如下 :
#main.py
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000) #指定 I2C 介面之 GPIO 與傳輸速率
lcd=I2cLcd(i2c, 0x27, 2, 16) #指定 I2C Slave 設備位址與顯示器之列數, 行數
lcd.putstr("Hello World!\nIt's working!") #顯示字串 (使用 "\n" 跳行)
接下來我想連線 NTP 伺服器, 取得目前的網路時間顯示在液晶螢幕上, 這就必須匯入 MicroPython 內建的 ntptime 與 time 模組, 參考 :
# MicroPython on ESP8266 (七) : 時間日期測試
測試 2 : 從 NTP 伺服器取得 UTC 時間轉成台灣時間顯示於 1602 上
#main.py
import time, ntptime
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
def fill_zero(n):
if n<10:
return '0' + str(n)
else:
return str(n)
<10: p="">
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
ntptime.settime() #從 NTP 伺服器取得 UTC 時間後設定本地時鐘
while True:
utc_epoch=time.mktime(time.localtime()) #將本地時鐘轉成 UTC 之時戳
Y,M,D,H,m,S,ms,W=time.localtime(utc_epoch + 28800) #UTC 加 8 小時=台灣時間
YMD='%s-%s-%s' % (str(Y),fill_zero(M),fill_zero(D)) #日期字串
HmS='%s:%s:%s' % (fill_zero(H),fill_zero(m),fill_zero(S)) #時間字串
lcd.move_to(0, 0) #移動游標到第一列第一行
lcd.putstr(YMD) #顯示日期
lcd.move_to(0, 1) #移動游標到第二列第一行
lcd.putstr(HmS) #顯示時間
time.sleep(1) #暫停 1 秒
此程式一開始執行時便利用 ntptime.settime() 透過網路從 NTP 伺服器取得 GMT 時戳 (單位:毫秒) , 並用此時戳來設定 ESP8266 內部時鐘, 接著就跳進無限迴圈每秒自內建時鐘讀取此時戳, 加上 28800 毫秒即得台灣時間時戳 (因台灣為 GMT + 8 小時, 8*60*60=28800), 然後用自訂函數 fill_zero() 將日期時間格式標準化, 即個位數字前面補 0, 再分別將日期與時間顯示於 1602 的第一列與第二列 :
10:>
從影片中可知在 38:00 時出現跳秒現象 (一次跳兩秒), 直接從 38:00 跳躍到 38:02. 雖然每秒去讀取時戳, 但由於運算需要些微時間, 累積起來就會造成秒差, 觀察大約每 10 秒就會出現跳秒現象.
注意, 上面這個測試是假定 ESP8266 已可連線到無線基地台上網際網路, ESP8266 會記住最近一次的 SSID 與 PWD, 一開機即可自動連線, 所以上面程式可直接存成 main.py 上傳 ESP8266. 如果需要修改連線之基地台, 例如移動到另一個環境無法連線最近一次設定之基地台, 可將上面程式存成 myapp.py, 然後參考下列這篇裡面的 main.py, 拿掉最後兩列前面的 # :
# WeMOS D1 Mini 開發板測試
#main.py
import time
WAIT_FOR_CONNECT=8
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])
else:
cs.send(html % form)
cs.close()
s.close()
import network
sta=network.WLAN(network.STA_IF)
sta.active(True)
print('Connecting to AP ...')
time.sleep(WAIT_FOR_CONNECT)
if not sta.isconnected():
set_ap()
else:
print('Connected:IP=', sta.ifconfig()[0])
#Application code is written here or import from a separate file
import myapp
myapp.main()
將此 main.py 與測試 2 的 myapp.py 上傳 ESP8266, 然後打開手機 WiFi 連線 ESP8266 內建 AP, 其 SSID 為 "MicroPython-" 開頭 (後面是 MAC 後 6 碼), 預設密碼 "micropythoN". 接著用手機瀏覽器 (建議用 Chrome) 連線網址 192.168.4.1 即可設定欲連線之無線基地台了.
D:\ESP8266\test>ampy --port COM8 put main.py
D:\ESP8266\test>ampy --port COM8 put myapp.py
REPL 輸出如下 :
PYB: soft reboot
#14 ets_task(40100164, 3, 3fff829c, 4)
WebREPL is not configured, run 'import webrepl_setup'
Connecting to AP ...
Connected:IP= 192.168.43.227
(2017, 8, 7, 1, 16, 32, 0, 219)
上面這個 8 元素的 tuple 是 ntptime.settime() 的傳回值, 分別表示 (年, 月, 日, 時, 分, 秒, 星期, 年日), 倒數第二個的星期值為 0~6, 其中 0 表示星期一, 6 表示星期天; 而年日表示這是一年中的第幾天, 參考 :
測試 3 : 每小時與 NTP 伺服器對時一次顯示台灣時間顯示於 1602 上
from machine import I2C, Pin
from esp8266_i2c_lcd import I2cLcd
def fill_zero(n):
if n<10:
return '0' + str(n)
else:
return str(n)
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
ntptime.settime() #與 NTP 伺服器初次對時
n=0 #設定重新對時計數器初始值
while True:
utc_epoch=time.mktime(time.localtime())
Y,M,D,H,m,S,ms,W=time.localtime(utc_epoch + 28800)
YMD='%s-%s-%s' % (str(Y),fill_zero(M),fill_zero(D))
HmS='%s:%s:%s' % (fill_zero(H),fill_zero(m),fill_zero(S))
lcd.move_to(0, 0)
lcd.putstr(YMD)
lcd.move_to(0, 1)
lcd.putstr(HmS)
time.sleep(1)
n=n+1 #計數器增量
if n >= 3600: #超過 1 小時 (3600 秒)
ntptime.settime() #重新對時 1 次
n=0 #計數器歸零重新計數
此程式添加了一個重新對時計數器 n 來記錄離上一次對時已經過了幾秒, 每次迴圈結束前會增量此計數器, 並檢查是否超過 3600, 是的話表示離上次與 NTP 對時已 1 小時 (因每個迴圈 1 秒, 3600 秒即 1 小時), 再次呼叫 ntptime.settime() 對時一次以更新 ESP8266 內部時鐘.
下面測試 4 我想加上 DHT11 溫溼度模組與光敏電阻來顯示溫溼度與亮度, 參考之前這篇的測試 5 (但現在還沒有要紀錄到 ThingSpeak 網站, 只是純粹顯示在 1602 上面而已) :
此外我想加入星期訊息, 從上面 REPL 輸出可知 ntptime.settime() 傳回的 tuple 倒數第二個為星期訊息, 0~6 分別代表星期一到星期六. 但液晶螢幕上的顯示區域有限, 所以我打算只顯示星期的英文前兩個字母當簡碼, 因為一個不足以辨認, 例如 Tuesday 與 Thursday, Saturday 與 Sunday 如果只用 T 與 S 表示就分不清楚.
| 傳回值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
| 星期 | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday |
| 簡碼 | Mo | Tu | We | Th | Fr | Sa | Su |
我將 1602 的顯示區域整體規劃如下 :
第一列先顯示日期, 緊接著是兩個字元的星期代碼, 然後空一格顯示亮度比率 (100% 為最亮). 第二列則是時間訊息後空一個顯示攝氏溫度, 再空一格顯示濕度.
import time, ntptime, dht
from machine import I2C, Pin, ADC
from esp8266_i2c_lcd import I2cLcd
def fill_zero(n): #個位數前面補 0
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),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
ntptime.settime()
n=0
DHTPIN=Pin(16, Pin.IN)
d=dht.DHT11(DHTPIN)
adc=ADC(0)
week={0:'Mo',1:'Tu',2:'We',3:'Th',4:'Fr',5:'Sa',6:'Su'}
while True:
d.measure()
t=d.temperature() #溫度
h=d.humidity() #濕度
a=adc.read() #讀取光敏電阻 ADC 值 (0~1024)
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)
YMDW='%s-%s-%s%s' % (str(Y),fill_zero(M),fill_zero(D),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)) #轉成濕度字串
lcd.move_to(0, 0)
lcd.putstr(YMDW) #顯示日期與星期
lcd.move_to(13, 0)
lcd.putstr(A) #顯示亮度 (%)
lcd.move_to(0, 1)
lcd.putstr(HmS) #顯示時間
lcd.move_to(9, 1)
lcd.putstr(T) #顯示攝氏溫度
lcd.move_to(13, 1)
lcd.putstr(H) #顯示濕度
time.sleep(1)
n=n+1
if n >= 3600:
ntptime.settime()
n=0
此程式新增了 fill_blank() 函數來處理亮度, 溫度, 濕度是個位數時的對其問題, 這時會在前面補空格以免重覆出現單位字元例如 6%% , 2CC, 或 8HH 等奇怪輸出. 而星期簡碼與傳回值的對應則使用字典來儲存並與 YMD 結合為 YMDW.
接下來要測試 1602 HD44780 內 CGRAM 的自定義記憶體的用法, 希望能將上圖中的攝氏溫度單位由 C 改為右上角一個小圈圈的度數符號, ASCII 中沒有此符號, 必須利用 CGRAM 的 custom_char() 功能自製字元的 Bitmap.
自訂字元用法參考上面 Github 下載檔案 i2c_lcd_test.py 裡面的範例, 是透過呼叫 I2cLcd 物件的 custom_char() 方法將 8 個 bytes 的二進位陣列寫入 0~7 的 CGRAM 位址中, 然後用 Python 內建函數 chr() 傳入 0~7 位址可提取此自訂字元.
# https://github.com/dhylands/python_lcd
1602 LCD 的每一個字元解析度是 5*8=40 px (不過最後一列似乎沒有使用), 我製作了兩種攝氏度數符號的 bitmap 如下, 一個靠左, 一個靠右 :
其儲存指令分別為 :
lcd.custom_char(0, bytearray([0x07,0x05,0x07,0x00,0x00,0x00,0x00,0x00])) #靠右
lcd.custom_char(0, bytearray([0x1C,0x14,0x1C,0x00,0x00,0x00,0x00,0x00])) #靠左
在下面測試 5 程式中, 我在 CGRAM 位址 0 加入了此攝氏度數符號字元, 修改了變數 T 的輸出格式用此自訂字元替換測試 4 中的 'C' 字元 :
測試 5 : 顯示日期時間, 溫溼度, 亮度於 1602 液晶顯示器 (使用自定義字元)
import time, ntptime, dht
from machine import I2C, Pin, ADC
from esp8266_i2c_lcd import I2cLcd
def fill_zero(n): #個位數前面補 0
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),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
lcd.custom_char(0, bytearray([0x1C,0x14,0x1C,0x00,0x00,0x00,0x00,0x00]))
DHTPIN=Pin(16, Pin.IN)
LEDPIN=Pin(14, Pin.OUT)
d=dht.DHT11(DHTPIN)
adc=ADC(0)
week={0:'Mo',1:'Tu',2:'We',3:'Th',4:'Fr',5:'Sa',6:'Su'}
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)
YMDW='%s-%s-%s%s' % (str(Y),fill_zero(M),fill_zero(D),week[W])
HmS='%s:%s:%s' % (fill_zero(H),fill_zero(m),fill_zero(S))
A='%s%%' % (fill_blank(a))
T='%s%s' % (fill_blank(t),chr(0))
H='%sH' % (fill_blank(h))
lcd.move_to(0, 0)
lcd.putstr(YMDW)
lcd.move_to(13, 0)
lcd.putstr(A)
lcd.move_to(0, 1)
lcd.putstr(HmS)
lcd.move_to(9, 1)
lcd.putstr(T)
lcd.move_to(13, 1)
lcd.putstr(H)
time.sleep(1)
n=n+1
if n >= 3600:
try:
ntptime.settime()
except:
pass
n=0
測試結果顯示, 似乎靠左跟度數近一點較好. 另外, 上面的程式碼中的 ntptime.settime() 改用 try except 包起來, 因為若網路中斷時呼叫 settime() 會出現例外 (OSError: -2) 而使程式當掉停止執行, 必須做例外處理, 當網路無法存取時就跳過 :
PYB: sof#9 ets_task(40100164, 3, 3fff829c, 4)
WebREPL is not configured, run 'import webrepl_setup'
Connecting to AP ...
Connected:IP= 192.168.43.227
Traceback (most recent call last):
File "main.py", line 86, in <module>
File "myapp.py", line 13, in <module>
File "ntptime.py", line 30, in settime
File "ntptime.py", line 18, in time
OSError: -2
觀察發現程式碼多了後, 跳秒情形似乎更明顯了, 大概每 4~6 秒就會跳一格. 其實秒數並不是很重要, 既然很難避免跳秒, 不如乾脆去除秒數, 只要顯示到分就好, 這樣就可以把時分與年月日這兩個時間相關訊息一起放在第一列剛好塞滿, 亮度就移到第二列顯示. 少了秒數後第二列就有點空, 所以我把星期簡碼由兩個字元改為三個字元, 星期字典也修改為 :
week={0:'Mon',1:'Tue',2:'Wed',3:'Thu',4:'Fri',5:'Sat',6:'Sun'}
| 傳回值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
| 星期 | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday |
| 簡碼 | Mon | Tue | Wed | Thu | Fri | Sat | Sun |
LCD 顯示區域重新規劃如下 :
我將測試 5 程式修改如下, 修改部分以藍色標示 :
測試 6 : 顯示日期時間, 溫溼度, 亮度於 1602 液晶顯示器 (不顯示秒數)
#myapp.pyimport time, ntptime, dht
from machine import I2C, Pin, ADC
from esp8266_i2c_lcd import I2cLcd
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),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
lcd.custom_char(0, bytearray([0x1C,0x14,0x1C,0x00,0x00,0x00,0x00,0x00]))
DHTPIN=Pin(16, Pin.IN)
LEDPIN=Pin(14, 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))
Hm='%s:%s' % (fill_zero(H),fill_zero(m))
A='%s%%' % (fill_blank(a))
T='%s%s' % (fill_blank(t),chr(0))
H='%sH' % (fill_blank(h))
lcd.move_to(0, 0)
lcd.putstr(YMD)
lcd.move_to(11, 0)
lcd.putstr(Hm)
lcd.move_to(0, 1)
lcd.putstr(week[W])
lcd.move_to(5, 1)
lcd.putstr(T)
lcd.move_to(9, 1)
lcd.putstr(H)
lcd.move_to(13, 1)
lcd.putstr(A)
time.sleep(1)
n=n+1
if n >= 3600:
try:
ntptime.settime()
except:
pass
n=0
呵呵, 這個配置看起來比較好看, 溫溼度亮度等環境資訊都在第二列, 而且我們通常只要知道幾點幾分就夠了, 秒數並不是很重要.
接下來要測試背光函數 backlight_on() 與 backlight_off(). 開啟背光功能大約會增加 20mA 左右的耗電, 基於省電考量, 我希望只有在偵測到附近有人經過時才開啟背光, 否則就關閉以節省用電, 這就需要一個 PIR 紅外線感測器, 關於 PIR 用法參考 :
# Arduino 測試 : PIR 紅外線移動偵測 (一)
# Arduino 測試 : PIR 紅外線移動偵測 (二)
# MicroPython on ESP8266 (八) : GPIO 測試
# MicroPython on ESP8266 (九) : PIR 紅外線移動偵測
注意, PIR 感測器的 VCC 上面是標 +5V, 雖然接 D1 Mini 的 3.3V 接腳似乎也可以運作, 但是感覺較易受雜訊影響, 建議接在 D1 Mini 的 5V 接腳較穩.
我將上面測試 6 程式加上了 PIR 感測器程式碼如下, 修改與增加部分以藍色標示 :
測試 7 : 顯示日期時間, 溫溼度, 亮度於 1602 液晶顯示器 (利用 PIR 紅外線控制背光)
import time, ntptime, dht
from machine import I2C, Pin, ADC
from esp8266_i2c_lcd import I2cLcd
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 int0(p):
global backlight_state
global triggerCount
global triggerLimit
backlight_state=1
if triggerCount < triggerLimit:
triggerCount=triggerCount + 2
PIRPIN=Pin(0, Pin.IN)
PIRPIN.irq(trigger=Pin.IRQ_RISING, handler=int0)
backlight_state=0
triggerCount=0
triggerLimit=5
i2c=I2C(scl=Pin(5),sda=Pin(4),freq=400000)
lcd=I2cLcd(i2c, 0x27, 2, 16)
lcd.custom_char(0, bytearray([0x1C,0x14,0x1C,0x00,0x00,0x00,0x00,0x00]))
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))
Hm='%s:%s' % (fill_zero(H),fill_zero(m))
A='%s%%' % (fill_blank(a))
T='%s%s' % (fill_blank(t),chr(0))
H='%sH' % (fill_blank(h))
lcd.move_to(0, 0)
lcd.putstr(YMD)
lcd.move_to(11, 0)
lcd.putstr(Hm)
lcd.move_to(0, 1)
lcd.putstr(week[W])
lcd.move_to(5, 1)
lcd.putstr(T)
lcd.move_to(9, 1)
lcd.putstr(H)
lcd.move_to(13, 1)
lcd.putstr(A)
if backlight_state==1:
lcd.backlight_on()
LEDPIN.value(0)
triggerCount=triggerCount - 1
if triggerCount <= 0:
backlight_state=0
else:
lcd.backlight_off()
LEDPIN.value(1)
print("IRQ Trigger Count:",triggerCount,'Backlight State:',backlight_state)
time.sleep(1)
n=n+1
if n >= 3600:
try:
ntptime.settime()
except:
pass
n=0
此程式使用 GPIO 0 (D1 Mini 的 D4 腳) 來接 PIR 感測器的輸出, 當 PIR 偵測到人體紅外線時會輸出一個 HIGH 位準, 持續約 0.5 秒左右, 可透過 PIR 背板上的一顆可變電阻調整 HIGH 持續時間, 但最長持續時間也是很有限 (約 3~5 秒), 使得 LCD 上的資訊還沒看仔細, 背光就暗掉了.
為了在持續移動觸發 (閉鎖時間 2.5 秒) 下讓 LCD 背光維持在亮燈狀態, 我參考之前在 PIR 感應照明控制測試中的作法, 利用 PIR 輸出的上升緣 (LOW -> HIGH) 觸發外部硬體中斷 (IRQ) 控制三個全域變數 backlight_state, triggerCount, 以及 triggerLimit 之值, 以便在連續移動觸發時能使背光持續點亮.
不過此處為了不影響每個迴圈大致維持每秒跑一圈, 我沒有使用 time.sleep() 來延長背光持續點亮時間, 而是每次 PIR 觸發時讓 triggerCount 增量 2 而非 1, 這樣雖然每次迴圈會讓 triggerCount 減量 1, 但因增量速度是減量速度的兩倍以上, 所以在持續觸發情況下 triggerCount 不會很快歸零導致背光忽明忽亮, 這樣就能維持背光在點亮狀態. 而 triggerLimit 的作用則是讓持續觸發有個天花板擋著, 讓加倍增量的 triggerCount 能夠收斂, 如同核反應堆中阻止中子數增生的碳棒一樣.
參考 :
# Python based library for talking to character based LCDs.
# Arduino 使用 1602 IIC(I2C) LCD 點陣液晶模組
# I2C Bus 簡介
# I2C Bus 與 SMBUS 有甚麼不同
# MicroPython Library for I2C 2x16 LCD Screens (for WiPy/LoPy/SiPy only)
# Bucknalla/micropython-i2c-lcd (for WiPy/LoPy/SiPy only)
# https://github.com/Bucknalla
# dhylands/python_lcd
2017-08-10 補充 :
由於本實驗使用 WeMOS D1 mini, 其 A0 (ADC) 接腳接受的電壓是 0~3.3V (與 ESP-12 模組的 0~1V 不同), 對應到 AD 轉換後的 0~1023, 所以此處的亮度偵測使用一個 CdS 光敏電阻與 10K 歐姆電阻分壓, A0 (ADC) 直接讀取 10K 歐姆電阻的壓降後取百分數, 其值 (0~100) 即與光度成正比了, 參考下面這篇的補充說明 :
# MicroPython on ESP8266 (十五) : 光敏電阻與 ADC 測試
標籤:
物聯網
,
ESP8266
,
MicroPython
,
Python
2017年8月6日 星期日
2017 年第 31 周記事 : XP 換成 Pi 3
本周小狐狸們暑假已過去一半, 要準備收心了. 我最擔心的還是菁菁的會考複習進度, 她對此不甚積極, 總要我三叮四囑才會去做. 二哥則是不去李麥克後, 英文不知如何幫他提升. 我這階段擔憂的大部分是她們是否專心學習, 以及是否有進展.
上周末因為颱風沒回鄉下, 昨天回到家發現果樹都被整枝得光禿禿, 波羅蜜, 龍眼, 芒果全被砍得只剩主幹, 原來是阿泉伯的朋友拿電鋸來幫爸修整, 但保留一些枝幹, 因他懂得接枝, 說下回有空要來幫我們接其他品種的芒果. 這番修整後感覺室內採光豁然開朗, 廚房, 臥室不須開燈都非常明亮.
早上去市集後路經市圖, 進去挖寶還真的讓我找到下面五本好書 :
鄉下那台 XP 電腦已無法開機, 但三星的液晶螢幕 (4:3 VGA) 使用迄今快 10 年還是很耐用, 我打算換成之前買的 Pi 3 作為物聯網智慧家居伺服器, 耗電少可 24 小時開機, 平時要上網看 Youtube 或瀏覽網頁是 OK 的, 隨時打開螢幕即可無需等待開機. 為此前兩周買了 HDMI 轉 VGA 線與 2 對 1 VGA 多工切換器, 但因上周颱風沒回去而無法測試.
下午等表哥他們回去後開始拔掉 XP 所有線頭, 順便整理桌面雜物, 最後將 Pi 3 接好開機, Bingo! 轉成的 VGA 影像還不錯, 接著把原先 XP 用的喇叭插入 HDMI-VGA 轉換線的聲音插口, 也是沒問題的, 還在 Pi 3 上重看了一次 Youtube 上這群人的電話詐騙篇. 另外測試 2 對 1 VGA 多工切換器功能也是正常的. 由於還沒設定好虛擬主機, 所以返回高雄之前還是把 Pi 3 關機, 反正開著也是空轉.
原本的黑色大機殼佔據了桌面左側, 感覺礙手礙腳, 如今變成小烏龜旁邊的小塑膠盒, 空間真的清爽不少. 本來預計傍晚還要給太陽能發電的蓄電池裝設負載 (LED 感應照明), 但今天時間太少, 下周吧!
上周末因為颱風沒回鄉下, 昨天回到家發現果樹都被整枝得光禿禿, 波羅蜜, 龍眼, 芒果全被砍得只剩主幹, 原來是阿泉伯的朋友拿電鋸來幫爸修整, 但保留一些枝幹, 因他懂得接枝, 說下回有空要來幫我們接其他品種的芒果. 這番修整後感覺室內採光豁然開朗, 廚房, 臥室不須開燈都非常明亮.
早上去市集後路經市圖, 進去挖寶還真的讓我找到下面五本好書 :
- Python+Spark 2.0+Hadoop 機器學習與大數據分析實戰
- 宋美齡-蔣介石的一號情報員
- 為什麼 Google 不夠用?
- 透視數據下的商機-運用 Google Analytics 發掘商業洞見
- HTML5-CSS3 最強圖解實戰講座
鄉下那台 XP 電腦已無法開機, 但三星的液晶螢幕 (4:3 VGA) 使用迄今快 10 年還是很耐用, 我打算換成之前買的 Pi 3 作為物聯網智慧家居伺服器, 耗電少可 24 小時開機, 平時要上網看 Youtube 或瀏覽網頁是 OK 的, 隨時打開螢幕即可無需等待開機. 為此前兩周買了 HDMI 轉 VGA 線與 2 對 1 VGA 多工切換器, 但因上周颱風沒回去而無法測試.
下午等表哥他們回去後開始拔掉 XP 所有線頭, 順便整理桌面雜物, 最後將 Pi 3 接好開機, Bingo! 轉成的 VGA 影像還不錯, 接著把原先 XP 用的喇叭插入 HDMI-VGA 轉換線的聲音插口, 也是沒問題的, 還在 Pi 3 上重看了一次 Youtube 上這群人的電話詐騙篇. 另外測試 2 對 1 VGA 多工切換器功能也是正常的. 由於還沒設定好虛擬主機, 所以返回高雄之前還是把 Pi 3 關機, 反正開著也是空轉.
原本的黑色大機殼佔據了桌面左側, 感覺礙手礙腳, 如今變成小烏龜旁邊的小塑膠盒, 空間真的清爽不少. 本來預計傍晚還要給太陽能發電的蓄電池裝設負載 (LED 感應照明), 但今天時間太少, 下周吧!
2017年8月5日 星期六
鄉村基礎木作第四課 : 抽屜底板與前板加工
早上到木樂園上第四課, 一踏入工場竟然除老師外都沒人, 今天是怎樣? 總之今天老師可輕鬆了, 除了不定時來看我做得如何跟講解下一進度外, 就在準備上課要用的木頭跟上網.
今天進度是繼續抽屜組裝, 上回完成邊框, 還沒裝上抽屜底板. 不過老師先拿出一個內彎型的線刨刀, 要我將抽屜的三個邊框上面外緣刨成圓弧形, 這樣摸起來才不會這麼利. 刨好後再用砂紙做細部加工, 同時抽屜上側內緣無法用刨刀, 只要用砂紙稍微磨得不要那麼利就好 :
接著裝上底板, 這是厚度約 4mm 的三合板, 先在邊框底部塗上膠, 把底板對齊後使用氣動釘槍來固定在抽屜邊框上.
由於底板一定會裁得比抽屜稍大些, 所以接下來要用刨刀把多出來的底板掉 :
刨好後也是用砂紙 "夕阿給" 一下. 最後是給抽屜前板用修邊機挖出一個方形紋路, 裝飾用的. 老師說花樣可自選, 我不想花俏, 就選了最簡單的 長方形 V 型溝. 這個做完就已經 12 點多了, 剩下的下周 2 或 4 晚上再去.
回來查了一下一把釘槍加上空壓機差不多 3000 元出頭. 博愛路鴻昌五金有賣 :
# 台灣製造 2HP8L 快速空壓機 直接式空壓機 空氣壓縮機 $2580
# 鴻昌五金 台灣製川島 1022J氣動釘槍 ㄇ型釘槍 ㄇ型氣動釘槍 $520
今天進度是繼續抽屜組裝, 上回完成邊框, 還沒裝上抽屜底板. 不過老師先拿出一個內彎型的線刨刀, 要我將抽屜的三個邊框上面外緣刨成圓弧形, 這樣摸起來才不會這麼利. 刨好後再用砂紙做細部加工, 同時抽屜上側內緣無法用刨刀, 只要用砂紙稍微磨得不要那麼利就好 :
接著裝上底板, 這是厚度約 4mm 的三合板, 先在邊框底部塗上膠, 把底板對齊後使用氣動釘槍來固定在抽屜邊框上.
由於底板一定會裁得比抽屜稍大些, 所以接下來要用刨刀把多出來的底板掉 :
刨好後也是用砂紙 "夕阿給" 一下. 最後是給抽屜前板用修邊機挖出一個方形紋路, 裝飾用的. 老師說花樣可自選, 我不想花俏, 就選了最簡單的 長方形 V 型溝. 這個做完就已經 12 點多了, 剩下的下周 2 或 4 晚上再去.
回來查了一下一把釘槍加上空壓機差不多 3000 元出頭. 博愛路鴻昌五金有賣 :
# 台灣製造 2HP8L 快速空壓機 直接式空壓機 空氣壓縮機 $2580
# 鴻昌五金 台灣製川島 1022J氣動釘槍 ㄇ型釘槍 ㄇ型氣動釘槍 $520
2017年8月3日 星期四
EPS8266 開發板 Wio Link 與 Wio Node
我在 SeeedStudio 網站上看到這款以 ESP8266 為基礎的 Wio Link 開發板, 它獨特的地方是採用 Groove 接頭來連接主控板與其他感測器模組, 而且將感測器以 Restful 包裝, 標榜不需要跳線焊接, 透過手機 App 操作即可快速佈署物聯網應用. Wio Link 接出三個 GPIO, 一個 ADC, 一個 UART, 以及一個 I2C 埠, 另外還有一個鋰電池接頭, 這對於小型物聯網應用應該足夠 :
# http://wiki.seeed.cc/Wio_Link/
# https://www.seeedstudio.com/Wio-Link-p-2604.html
另外它也有迷你板 Wio Node, 只有兩個 Groove 接頭, 提供兩個 GPIO, 一個 ADC, 一個 UART, 以及兩個 I2C 埠, 另外還有一個鋰電池接頭 :
# [Wio Node] 連接教學 (新)
# [Wio Node] Grove Button 按鈕與事件監測
不過 Wio Link 單價 14 元美金, 折合台幣約 420 元, 露天最便宜賣 410 :
# 原裝 WIO LINK WiFi 物聯網、手機智能遙控、家庭自動化 ESP8266 $410
# Wio Node 迷你 ESP8266 開發版 $335
不管是 Wio Link 還是 Node 都太貴了, 比起 D1 mini 的 70~80 元太不划算. 我個人覺得還是用杜邦線接一接算了. Wio Link/Node 適合非本科系的物聯網學習者, 網頁設計師應該會蠻喜歡此產品, 因為 JSON 是再熟也不過的.
# http://wiki.seeed.cc/Wio_Link/
# https://www.seeedstudio.com/Wio-Link-p-2604.html
另外它也有迷你板 Wio Node, 只有兩個 Groove 接頭, 提供兩個 GPIO, 一個 ADC, 一個 UART, 以及兩個 I2C 埠, 另外還有一個鋰電池接頭 :
# [Wio Node] 連接教學 (新)
# [Wio Node] Grove Button 按鈕與事件監測
不過 Wio Link 單價 14 元美金, 折合台幣約 420 元, 露天最便宜賣 410 :
# 原裝 WIO LINK WiFi 物聯網、手機智能遙控、家庭自動化 ESP8266 $410
# Wio Node 迷你 ESP8266 開發版 $335
不管是 Wio Link 還是 Node 都太貴了, 比起 D1 mini 的 70~80 元太不划算. 我個人覺得還是用杜邦線接一接算了. Wio Link/Node 適合非本科系的物聯網學習者, 網頁設計師應該會蠻喜歡此產品, 因為 JSON 是再熟也不過的.
向城邦購買人工智慧套書兩冊
今天在城邦下訂購買庫茲威爾的 "人工智慧的未來" 與松尾豐的 "了解人工智慧的第一本書", 後者我已看完, 前者還在閱讀中. 雖然圖書館都借得到, 但是庫茲威爾這本值得收藏, 松尾豐的也值得參考, 剛好城邦將其組成套書優惠出售, 所以就買下來了 :
543/860 相當於是打 63 折, 接近買一送一了, 很划算. 參考 :
# 好書 : 人工智慧的未來
543/860 相當於是打 63 折, 接近買一送一了, 很划算. 參考 :
# 好書 : 人工智慧的未來
2017年8月2日 星期三
三兩三
這幾天跟同事閒聊時扯到 "沒有三兩三, 怎敢上梁山" 這句俗話, 以前一直認為意思是沒有一點本事的話是沒資格上梁山當土匪. 問題是為何是三兩三? 是指命格的重量嗎? 查谷歌找到 YAHOO 知識這則說明很有趣 :
# 沒有三兩三怎趕上梁山??
原來三兩三是指膽量的意思, 因為膽的重量三兩三銖, 借三兩三比喻有膽量之意. 底下有人促狹地回應, 梁山有山賊, 上梁山要帶些錢, 難道三兩三是梁山的低消?
# 沒有三兩三怎趕上梁山??
原來三兩三是指膽量的意思, 因為膽的重量三兩三銖, 借三兩三比喻有膽量之意. 底下有人促狹地回應, 梁山有山賊, 上梁山要帶些錢, 難道三兩三是梁山的低消?
訂閱:
文章
(
Atom
)



















