從零開始自幹房間自動化燈光系統(Part 1)
說到我房間裡的 IoT 控制系統,從 2021 年起我就一直在用一套自己開發的 IoT 協議名為 Home Dynamic。這套系統的原理和詳情可在我另一篇文章( 從零開始的 IoT 系統設計 )裡找到,但是簡單來說它就是一個很簡單的 HTTP RESTful request 模型下的迷你 IoT 系統,可以用於開關電燈和透過 Tasmota firmware 來控制幾個 Sonoff 的 WiFi Smart Switch 而已。 到最近,因為我香港的房間要整修,所以我就把房間裡有點 hacky 的系統升級到新的自動化系統。Home Dynamic v2 (簡稱 HDSv2)雖然在設計的時候已經有考慮到很多不同的使用場景和可能出現的問題(如 off grid control,伺服器 / relay 的 SPOF 等等),但是其控制 latency 總是讓我覺得有點煩惱。即使後來 ArozOS 改用 Golang 重寫,從按按鈕到電燈真的亮起的時間大概也只是在 2 - 3 秒內,這個速度跟我去按一個物理開關還是有很明顯的差別。所以在新裝潢的房間裡,我採用了一個更新版本的 HDS 協議來開發我的新自動化系統,現在我把它暫稱為 HDSv3。 控制器硬體部分 那說到 IoT 跟自動化,在提軟體之前一定要先講講硬體。畢竟只要硬體夠強,軟體再爛也是能跑得很順暢(望向 python) 。這次為了未來的擴展性和標準化,這次我選用了半工業級的硬體。下圖中是我用的繼電器模組,是一片市售的 4 路 WiFi 繼電器模組。它是使用 ESP12E 作為主控制器,跟先前的 HDSv2 一樣都是基於相同的 MCU。因此大部分 HDSv2 的源碼能夠直接移植到 v3 然後下載到這片板子上。雖然這片東西可以使用 110 - 220V 輸入,但是因為我原先整套系統都是用 24V 的,因此這部分的機能就白白被我浪費掉了( 那當然,這片東西不能就這樣放在我的桌面上。因此我買了一個 DIN Rail 用的外殼,然後把整個組好的安裝到 DIN Rail 上。 現時的照明控制系統 那你可能會好奇,既然是控制燈光,那一個簡單的繼電器模組就可以做到了。旁邊那兩個盒子又是甚麼? 如果你仔細看一下那個繼電器模組的照片,你就會發現那個模組真的單純在做切換而已,它本身並沒有供電能力。所以為了讓接上去的 LED 燈條有電力供應,就必須透過另一個裝置為 Relay 的 NO (Normally Open)端供電。這裡中間的模組就是作為一個 power distribution hub 的用法來提供 4 個相同於 Vin 的輸出和 3 個可調節的輸出。 大概原理 而最左邊的是 LED strip 的限流模組,大概就是因為 24V 的燈條用 24V 供電的話會太光,所以那個小黑色模組裡面是一個簡單的 XL6009 自動升降壓模組來把電壓調整到大約 19.2V (我喜歡的亮度) 原本是長這樣的,後來想了想還是把它放到 DIN Rail 上比較好 軟體與控制部分 講到軟體部分,就不得不提一下 IoT 技術了。說真的,雖然我知道很多不同的 IoT 通訊協議,從 MQTT(DIY 很多人用,基於 TCP/IP)、 Zigbee (IKEA 那套)到工業用的 RS485 / RS232 之類的,我還是比較喜歡用 2.4Ghz WiFi。雖然距離短、用電量高,但是本來我的裝置就是用來控制電源的(aka 長期會有電源供應),加上香港地方實在太少,我想盡量省下一個放 hub 的空間 + 本身房間已經有 WiFi 覆蓋,所以自然地 WiFi 是最好的選擇,所以我還是選擇了基於 TCP/IP 的協議。 考慮到設備的相容性、不用額外安裝程式便能控制等優點,在眾多基於 TCP/IP 的協議下我選擇了 RESTFUL。一來是所有手機都有網頁瀏覽器,即使伺服器炸掉只要有一台手機能連接到同一網絡便可以透過 p2p 的方式控制 IoT 裝置。另一個好處是 RESTFUL API 實在太通用了,從 ESPHome 到 Home Assitant,最好支援的裝置類型依然是基於 RESTFUL API 的裝置,再加上怎樣看 HTTP request 這東西在未來 10 年應該都會還存在(畢竟是 Web 技術的基礎),選它一定沒錯( 而為了解決…
在 Linux 上建立一個有容量限制的資料夾
最近我在研究 mdadm 的時候無意中發現了一個可以依靠 losetup 和 partition 映像檔的神奇方法來建立一個具有容量限制的資料夾。所以就寫這篇來簡單記錄一下。 好,可是這有甚麼用? 之前我在開發 ArozOS 的時候想著給使用者建立一個 storage quota。本來想著用 linux 的 File System 來 implement 這個功能,可是找了很久之後發現方法都很麻煩或是過於複雜(例如依賴一些特別的 file system format / container)。所以如果剛好你想設置一個 hard limit 給一些軟體來做 buffer / upload 之類的,都可以考慮這個方法。 首先,建立一個映像檔 我們第一件事情要做的就是建立一個固定大小的映像檔。以我這裡為例,我建立了一個 64MB 的映像檔來模擬一個硬碟 partition。 bs 是 block size,就是系統會 buffer 多少 data 才會真正寫進去這個(block )device,這裡用的是 4MB。 count 是這個虛擬硬碟裡有多少個 block,16 個 4MB 的 block 加起來就是 64MB 啦 sudo dd if/dev/zero of=sdX.img bs=4M count=16 至於 /dev/zero 是甚麼,它是一個只會不斷吐出 0 的 byte stream。你可以把它當成一個無限大的檔案,裡面只存在無限個 0。透過 dd 指令,我們就可以把需要的 0 bit 從這個檔案裡 clone 出來來建立我們需要的映像檔大小。 之後:建立檔案系統 在建立了一個空白的映像檔之後,透過 ls 指令我們可以看到新鮮的 .img 檔出現了。 aroz@orangepizero2:~/raidtest$ ls sdX.img 然後我們就是需要對它進行格式化,就古人所說 Everything is a file Linus Torvalds did not say this 所以我們可以直接對它用 mkfs 跟 mount 指令。假設我們有一個叫 sdX/ 的資料夾,那我們就可以把映像檔格式化之後掛到那個資料夾。 // 建立掛載點 aroz@orangepizero2:~/raidtest$ mkdir sdX //用 ext4 把映像檔格式化 aroz@orangepizero2:~/raidtest$ sudo mkfs.ext4 sdX.img mke2fs 1.47.0 (5-Feb-2023) Discarding device blocks: done Creating filesystem with 65536 1k blocks and 16384 inodes Filesystem UUID: bf600d34-c93a-48bc-ad27-27255fbd1333 Superblock backups stored on blocks: 8193, 24577, 40961, 57345 Allocating group tables: done Writing inode tables: done Creating journal (4096 blocks): done Writing superblocks and filesystem accounting information: done //檢查看看是不是需要的資料夾和檔案都存在 aroz@orangepizero2:~/raidtest$ ls sdX sdX.img //把映像檔掛上去 aroz@orangepizero2:~/raidtest$ sudo mount sdX.img ./sdX 之後我們就可以在 df 裡看到掛載的虛擬硬碟 /…
在 Zoraxy 加入 TLS SNI 功能
最近我在開發的 Zoraxy v3 在我開發 Zoraxy 之前我也不知道 https 證書這東西對於 reverse proxy 伺服器來說到底有多複雜。一開始的時候因為我都是用一個域名(domain name),所以 reverse proxy 裡面也是只需要處理一個主機名稱(host name)。而初代的 Zoraxy 則是直接用 TLS hello info 裡面的 server name 作為 certificate 的 key 來找到合適的 certificate 並回傳給客戶端,所以時間複雜度(time complexity)而言是 O(1) 的速度。換成程式碼大概長這樣: http.ListenAndServeTLS(":443", "server.crt", "server.key", nil) 到後來發現 virtual directory 帶來的麻煩後,Zoraxy v2 加入了 sub-domain 的支援。為了解決找 certificate 的問題, v2 的做法是讓使用者把每個 sub-domain 都 map 到一張 certificate 上面,所以結果就是同一個域名下會有很多張證書(例如說 a.example.com 會有 a.example.com 的證書、b.example.com 會有 b.example.com 的證書)。這樣設計的好處是一來找 certificate 的演算法比較簡單,二來我們可以根據 hello info 的 server name 進行也是 O(1) 時間複雜度的 certificate lookup,不用任何存取 file system 的 loop 便可以直接找到證書。 if fileExists(helloInfo.ServerName+".pem") && fileExists( helloInfo.ServerName+".key") { //Direct hit pubKey = helloInfo.ServerName+".pem" priKey = helloInfo.ServerName+".key" } 然後在 Zoraxy v2 用了這一年多之後就又出現問題了。coauthor 的其中一張證書裡面包含了幾個不同的 domain 跟 subdomain,也有使用者的證書是含 wildcard 的,也有是舊版用 Common Name 來定義(而不是 DNS entry)來標記 host name 的,結果就是一大堆 issue 就出現在 repo 上面。 SNI 的基本概念 SNI 的原理大概就是由伺服器端跟據 TLS Hello Info 的 Server Name 來自動回傳合適的 certificate 給 Client (這裡因為 Zoraxy 是一個 http proxy,那我就以瀏覽器為 client 的例子)。對於很直接的域名例如說 v1 跟 v2 裡面出現的,基本上就是只要把 certificate map 到一個 string 轉 certificate 的資料結構裡面就可以了。但是對於 v3 之後的複雜案例,則是需要一些更複雜的邏輯來處理。 相信來得我部落格的人不是工程師應該都是技術大佬,所以我就直接把 code 拿出來講好了。這裡是 Zoraxy v3 TLS 解釋器裡面最重要的 function func(m * Manager) CertMatchExists(serverName string) bool { for _, certCacheEntry: = range m.LoadedCerts { if certCacheEntry.Cert.VerifyHostname(serverName) == nil || certCacheEntry.Cert.Issuer.CommonName ==…
ArozOS 的桌面大更新
之前有一段時間我都在想幫 ArozOS 的桌面做一個大更新,把最頂部的仿 Ubuntu 狀態欄拿掉,可是因為開刀和學業上各種事情一直沒空完成。最近新年前剛好指導教授不在一個禮拜,所以我就來更新了一下這個久久沒時間完成的小目標。 原先 web-desktop 的界面,上下都有一個黑色的功能欄 新的 web-desktop 界面,最頂部實色的被整合到下方的欄內 日曆和通知也被移到右下方 右下方角落的那個圈圈就是以前在右上方的背景執行工作列表 其中我最喜歡的功能大概就是這個系統一覽表了。從伺服器的在線狀態,名稱,SMART 狀態到各顆硬碟的使用量都能夠直接看清楚。 嘛當然除了這個以外還加入了很多微細的使用者體驗最佳化,例如說背景加深 + 模糊的狀態來突顯某個元件之類的 未打開日曆前的背景 打開日曆後加入的 backdrop-filter blur,突出了日曆和通知欄的前置感 另外也加入了能直接進入進階分享設定的功能按鈕,比起以往要進入到 File Manager 來更改進階設定要來得更方便。 這樣的界面大概已經算得上是追到近年的 web-desktop 界面水平了吧?雖然說在 github web-desktop 的分類排行中 ArozOS 是掉到了第四位,但是以完整性來說 ArozOS 還是跟 DSM 比得上的 web-desktop 類界面(
Golang – 加速抓資料夾的總大小的方法
最近因為一些意外所以要頻繁跑醫院回診,結果就是在等待跟來回的時間前後多了很多零碎的時間,所以我就不浪費這些時間來幫我用了快 6 - 7 年的 ArozOS 系統做了點更新了。但是這篇部落格並不是在講我更新了甚麼(雖然某程度上也是),而是講一下關於 Golang 處理 exec 輸出的一個小發現。 背景故事 - ArozOS 的 Disk Properties 載很慢 ArozOS 的 Disk Properties ArozOS 因為採用半虛擬磁碟架構的設計,所以每次要列出 disk space 的時候,除了一般像 Windows 那樣會列出用了多少跟全部空間是多少以外,如果磁碟空間是使用者分隔的,就要多計算使用者佔用的空間比(圖上黃色條) 這個功能原本是使用 Walk function 來實現的。因為 ArozOS 的 vfs architecture 關系,這裡我使用的是 ArozOS 專用的 filepath module (你可以把它想像成 Go 原本的 filepath.Walk var size int64 = 0 fshAbs.Walk(rpath, func(_ string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { size += info.Size() } return err }) filesize = size fshAbs 是 file system handler abstraction 的意思,大概就是「這個虛擬磁碟的檔案系統層」的 object。 另外 rpath 就是從 user space translate 過來到這個 abstraction layer 的實質路徑,如果是本機磁碟的話就會是好像 /media/storage1/users/{username}/myfile.txt 這樣的 path;如果是 network drive 的話會是跟 mount-point 的相對路徑,例如 WebDAV 或是 FTP 下就可能會出現 /myfile.txt,或是 SMB 下的 /Share/myfile.txt 之類。 這樣的 implementation 你一看就可以知道這個在 Network Drive 上是不可能實現的,因為每一個檔案 Go 都要抓它的 Stat,即是遠端有多少個檔案就會送多少個 file request 過去,所以自很早之前就 ArozOS 的 File Manager 就加入了 這個簡單粗暴的解決方法… if fsh.IsNetworkDrive() { filesize = -1 } 然而隨著我在用的 NAS 東西越來越多,這個 walk function 即使在 local disk 下也開始變得很慢。 那改用 kernel 內置的 API 來抓不就好了嗎? 確實如此。Linux kernel 裡其實有內置一個滿好用的指令叫 du,用起來也超簡單,只要: du -sb /media/storage1 481491222874 /media/storage1 那這個資料夾總大小(byte size)就出來了。如果你想要 block size 的話可以把 b 去掉。然後就是寫一個簡單的 Go function 去把資料抓出來。 /* Get Directory Size with native…
透過 JLC PCBA 特殊零件的方法 – Consign Parts
最近我在開發一個全自動 USB TYPE C 的回流焊加熱板,需要用到一顆特殊的 PD trigger IC - IP2721 (TSSOP-16),雖然它是存在於 JLC Library 裡面,可是因為全球性供貨問題而導致沒存貨。 Order Parts - 要求訂購零件 JLC 提供一個功能叫 Order Parts,但是它並不是訂購然後附在 PCB 盒子裡給你,而是只限於用在 PCBA 上的。這個就比較簡單,當你在 PCBA 下單的時候看到需要的零件沒貨,就可以先取消訂單,然後到 Parts Order 頁面下單你需要的零件 但是由於這顆 IC 實在太難找,所以在 Order Part 所有 Supplier 都沒有的情況下,我要求了一個新的 part request。 Part Request 的流程大概這樣: 填寫你要求的零件 Mft Part Number ( 原廠零件編號 ,例:IP2721)跟 package (封裝規格,例:TSSOP-16)它會預估一個金額給你(例:0.5590 / pcs),(這個金額是預估的,如果報價不準我猜他們應該會自行吸收或是要你補差價?)並以這個金額付款訂購它會進入一個叫 Quotation 的流程,一般來說這會卡個 2 - 3 天然後如果成功 quotation 的話應該很快就會出現在你的個人 part list 了。 然而由於這顆 IC 實在到處都沒貨,所以最後還是收到了 JLC 的退款。。。 那問題就來了: 如果連他們也沒辦法 source 到這顆 IC,到底還有甚麼辦法可以買到這顆東西呢? 不說不知道,還真的有。 JLC 還備有最後一個 fallback 用的功能,就是 Consign parts。簡單來說就是你可以透過第三方郵寄材料到他們的 SMD 倉庫,然後再由他們手動加入到你的 part list。 做法也是很簡單,首先到 Consigned Part List 裡找到你要的零件 Mft 號碼跟 package,然後點 Add to Consignment 然後只要根據他提供的地址把零件送過去就可以了。說起來很簡單,但是最困難的部分還沒出現:就是採購和運輸。 在選擇好之後會顯示個人化的收貨地址,由於含個人資料所以這裡我是抓 JLC 網站的範例圖 你看,PCBA 這東西當然是不可能靠人手來做而是使用 PnP 機器來做。因此例如散裝、袋裝之類的零件,他們是沒辦法用的。對於這個,他們的客服跟我說如果送過去的包裝不對也能貼,但是會每顆收取 0.14USD 的手工貼片服務費,以通俗的說話來講就是「你他媽的最好不要送錯喔」 他們接收而免手續費的零件包裝種類只有兩種:1. 編帶 (一條 (不能是散裝多條用膠帶黏起來)或 一整卷編帶 )2. 托盤 那了解這個之後就可以訂零件了。由於 JLC 只接收中國大陸內的物流,所以這個時候就需要聯系華強北的專家來處理了(還好平常進口電子零件的時候收集的店家名片沒扔掉)。簡單來說就是提供你要的零件 Mft 號碼、 package 、數量,跟指定一定要單一條編帶 + 防靜電袋 + 寫上 Mft No. 發貨即可。雖然說他們建議使用 SF 物流,但是基本上只要能送到即可。 大概 3 - 4 天之後就出現在 Consignment part list 裡面 最後只要在 PCBA 的時候選擇從 My Parts 裡面抓這顆零件就可以了~ 我的板子終於要生產了啦 備注 備用件 有時候部分零件 package size 是屬於容易貼錯的大小,在你第一次 PCBA preview 的時候它會出現建議數量 > 實際所需數量的情況(例如說這裡我只做 50 片,IRLR8726T 卻要求你給 52 片) 如果這個情況剛好出現在你的 Consignment parts 上面,記得多買幾顆送過去。一般來說中國買的集成電路(含郵票孔類的模組,除非你是從一些大廠那邊買)類產品會有 8 - 10% 的不良率,所以多準備 10% 應該是足夠的了。 DIP 與 SMT 價差 如果想省錢的話,把需要插件的零件都從 BOM 表中拿掉可以省不少喔!(不過更佳的做法當然是在設計電路板的時候盡可能換成…
WebStick – 基於 ESP8266 的網頁伺服器棒棒
自從我看了 ESP8266 的 SD Web Server Youtube 教學之後,我一直都很想要自己做一個來玩玩看。你想想看,有一根可以隨身攜帶(?)、足夠便宜到可以送人而且能夠把虛擬的東西(網站)實體化,不是一件很有趣的事情嗎? 可是之前一直都沒有時間研究這個東西,其中一個原因是以前的焊接技術還沒足夠讓我可以直的完成這個 project,另外就是因為那個時候我對燒錄器設計還是沒有甚麼概念,導致到當我要做到跟一片 Wemos D1 mini 同等效能的 ESP8266 的時候,完全沒辦法好好的設計和焊接成成品出來。 開坑 早在 2018 年,我就已經買來了一片 Wemos D1 mini 跟超級難買的 SD shield 來當小型網頁伺服器。當時的我對網頁編程和嵌入式開發還是非常的入門,結果做出來的東西就是長這樣 Wemos D1 + SD Shield + 小容量 SD 卡 雖然是能用,而且我也把它用 USB 供電吊在我的桌面旁邊當作實驗品開發了一段時間,但是由於用的是別人的 code 加上當時我並不是太熟悉 Arduino C++ (和當時還沒有 ChatGPT),導致最後開發出來的東西不但不好看而且很難用 設計嘗試,失敗收場 之後一年我試著把這個設計弄成一片真正的電路板。由於當時我還沒有開發燒錄器的能力(特別是要想辦燒焊那兩個超小的 BC817 transistor),因此我的想法是先把 ESP8266 焊到板子上,再透過現成的燒錄器對它進行開發(見下面那一排燒錄用排針),然而後來我弄著弄著就覺得不對勁,而且這種每次都要插燒錄器才能寫東西的系統感覺開發起來就很麻煩,應該不會有人想用,所以最後還是沒拿來生產。 第一代的 ESP8266 網頁伺服器,最後沒做生產真是一個明智的決定 5 年之後,我終於做出了 WebStick 原型機 WebStick 這東西其實在我腦海中已經卡住很多年了。但是一直就軟硬體技術原因無法開坑。直到最近開始學會了 drag soldering 跟找到了一款可以讓我進行超精密焊接(但是缺點是一次性)的烙鐵頭後,我終於把這個封塵多年的 project 重新抓出來做。 首先:燒錄器 網上有很多不同的設計,有自動的也有半自動(燒錄的時候要按 FLASH 按鈕)。半自動的能省下兩顆 transistor 跟兩顆電阻,但是由於編程的時候每次都要按 FLASH 和 RESET 有夠麻煩,所以我在設計的時候便選擇了自動燒錄器。以下是我參考的設計圖: 燒錄器設計圖 但是這設計圖差了點東西,就是圖裡沒有標注到 GPIO15 應該要 PULL LOW。在設計電路板的時候記得把 GPIO15 經電阻(如 10k)接地,這樣 SPI CS 才能正常使用。 為甚麼我會知道? 這真是個他媽的好問題(被坑) 這顆電阻雖然不加也可以,但是會無法使用 SPI 另外這兩顆 BC817 的 routing 也弄得我懷疑人生,不過後來還是在板上 route 好了。 我都不知道自己當晚是怎樣發神經想到可以這樣 route 的,嘛總之能動就好了。 其次:SD 卡 ESP8266 有內置的 SPI 針腳可以用來讀取 SD 卡。很多人以為 SD 卡模組上面有特別 IC 去把 SD 卡轉換成 ESP8266 可以讀取的信號,然而實際上的讀取方法聽上去有點奇妙,就是直接把 SD 卡當成 SPI Slave Device 來讀取。 那你會好奇,如果 SD 卡可以直接接 ESP8266,那外面在賣的 SD 卡模組上面那顆 IC 是幹甚麼用的?這是個好問題,那是防呆用的(不對) 你看,不是所有 MCU 都是在用 3.3V Logic Level 的說。例如說 Arduino UNO 在用的 Atmega328 就是支援 3.3v - 5v 輸入。SD 卡只能用 3.3V 讀取,過高的通訊電壓會把它弄壞,因此很多模組為了相容更多的開發板,只好加上一顆用來轉換信號線電壓的 IC,通稱 LLC (Logic Level Converter ,大陸好像叫 「電平轉換器」)。但是由於我在用的 ESP12E 只支援 3.3V,所以剛好與 SD 卡需要的電壓一樣,所以就不用轉直接接 MCU 就好啦。 不說不知道,如果你走去翻 Wemos D1 SD shield 的設計電路圖,它基本上就只有一個 SD 卡插槽,跟一顆差不多看不見大小的電容來做 ripple filter。真的有夠簡單欸 把兩個東西整合理起 當兩個電路都有了,剩下的部分就是把電路給整合到同一片板子上。我本來是打算用名片大小的設計,這樣做不但能有一種「這是送你的見面禮」感覺,而且也有更多的空間來寫使用教學,有點像國外嵌入式工程師大神做的能跑 Linux 的名片一樣 國外工程師做的,能跑 Linux…
用 CH552G 做一個數字機械鍵盤
在先前的文章中,我簡單的用之前的 macropad 零件做出來了一個數字鍵盤的原型機,但是因為那個只是功能測試的原型機,沒有進行使用者體驗測試,所以在成為最終設計之前,我還要修整一點東西。 首先:白色! 作為一個機械鍵盤,沒甚麼特色的話很難去吸引到人去注目它。因此在這次的設計裡,我決定做我一直以來都很想試試看的設計:全白! 除了鍵帽以外,連電路板都是白白的,超級好看的。 另外這片電路板設計還有一個特色,就是如果你是喜歡用 macro-pad 的人,你也可以用 macro-numpad 的 layout 來組裝這個鍵盤,讓同一個電路板設計有兩種不同的使用方法。 左:數字鍵盤;右:macro numpad 但是,這樣組裝起來按一下就發現了一點體感上的問題,就是比較長的按鈕在按下的時候會連帶下面的機械鍵一起偏斜。我猜是因為本身機械軸的高度問題,讓很多外面的機械鍵盤都會特別在按鍵跟 PCB 之間加入一層金屬板作為固定層。 不過作為一個超便宜的 DIY 方案,要是 CNC 一片這樣的金屬片就太貴了;作為替代方案,我這裡只當按鈕面積比較大的 2U 鍵進行加固,使用原先預留給 macropad 的按鈕固定孔為固定點,加入了一片特別的 3D 列印件以做到跟金融板同樣的功能。 完成 這樣簡單的搞一搞之後,我的 DIY 機械數字鍵盤就做好了。不知道是因為我還沒習慣機械鍵盤還是甚麼的原因,用這個鍵盤很容易打錯(?),或許下一次我可以用更薄的按鍵和鍵帽來多做一個試試看。
ArozOS 的多帳號切換系統原理
講到多帳戶切換系統,通常你只會在一些類似 AWS 或者 Google 那樣大規模的系統裡面才會看到,然而這並不是沒有原因的。因為有時候部分功能為了權限上的分別,不少人比起想要設定一個擁有多權限群組的用戶,反而會比較喜歡以帳戶的方式來使用不同權限群組的作業環境。 早在好幾十個版本之前,ArozOS 就可以建立不同的權限群組而且讓一個用戶加入不同的群組 單用戶多權限群組設計 原先的 ArozOS 採用多用戶多權限群組的設計,簡單來說就是每個用戶都可以加入一個或以上的權限群組,而這個用戶能做的事情和存取的儲存裝置就是 UNION of 所有的權限群組。假設群組 A 只能存取 A 的儲存池,群組 B 只能存取 B 的儲存池,那一個用戶加入了 A 和 B 的群組之後便能存取 A 和 B 兩個儲存池的資料,並在此中間自由移動和開啟檔案。 然而,不知道是不是這設計對於現代人來說太複雜了,很多人就是不會用,然後問我到底要怎樣才可以建立更多自用的使用者帳戶。建立之後他們又會來問到底要怎樣才可以把檔案在用戶之間移動,我就一整個(嗯??? 多用戶單權限群組設計 所以後來我在跟用戶訪談的時候,發現其實他們比較多在用多用戶單群組的用法。簡單來說,就是一個用戶只有一個權限群組,當他們想要存取另一個權限群組的資料的時候,他們便會登出原先帳戶並登入另一個帳戶。這用法通常是用來處理一些對外的服務(例如 www 的帳號來處理跟 web service 相關的檔案),但是用在 cloud desktop 上的這種要求我猜我應該是第一個 implement 的說? 所以為了體驗這種奇怪的使用方法,我特別在 test bench 上架了一個網站,讓 www 帳戶來保存檔案。 在 www 上架的一個 html file 然後就開始設計帳戶切換的邏輯。首先,一般的帳戶切換做法是使用 client side cookie / session 對每個帳戶進行登入,然後讓用戶可以自由切換在該 browser 上任何未過期的 login session。然而,在 ArozOS 上這有點不太可行 ArozOS 的 authentication 是基於 cookie 而非 localStorage。如果設計改了的話對一些依賴 legacy login API 的系統會帶來嚴重問題ArozOS 是網頁桌面系統,相比起其他網頁的 stateless 設計,ArozOS 的桌面是 stateful 的,而且整個狀態是儲存在伺服器端部分用戶在 ArozOS 上使用 oAuth / LDAP 作為登入認證伺服器,加上之後有打算加入 Cluster Authentication System 等伺服器端較複雜的結構,在客戶端上儲存登入狀態可能比較難做到跨伺服器間的 user state transfer 所以結合上述原因,我們最後採用了一個叫 Switchable Accounts Pool 的設計。 Switchable Accounts Pool (SAP) 是一個我們設計出來的實驗性東西。它的功能大概就是以一個 pool id 來對應一堆可以切換的帳戶,而這些帳戶又會有 main / secondary 的關系,讓帳戶的登出的時候在伺服器端對該瀏覽器的登入狀態進行切換。 一開始,當主帳戶登入的時候,一個在伺服器端的 SAP 便會被建立。Creator 的名字就會寫著是主帳戶的名字。之後,用戶可以透過登入 sub-account 以取得在這個 session 結束之前,可以不斷切換到 sub-account 的權利。 當 Sub account 登出的時候,由於 main account 的 session 仍存在,因此伺服器端會直接改寫 User 的 login cookie 到原先的 creator account。 而當 Main account (即 SAP 的 Creator)登出的時候,伺服器端便會直接 discard 整個 SAP 以保護其他 sub-account 的安全(非法透過登入 sub-account 以取得 main account 的存取權)。 在邏輯搞清楚之後,剩下的便是把功能寫出來了。這裡只是一點簡單的 Go programming,技術上沒甚麼特別的所以我就不再詳談。 網頁桌面上的可切換帳戶 新增帳戶的時候需要使用密碼登入,如果是近來切換過的而 session 沒過期可以透過伺服器端免密碼切換 在一些特殊情況下,例如說 main account 的 login session expire 但是 SAP 的 session 還沒 expire ,使用者便會在登入界面看到這樣的一個 popup,讓他可以快速回復之前登入過的…
在不更改電路板 BOM 表下把 Macropad 變成 Numpad
話說幾個月之前,我設計並開源了一個四鍵的 macropad (快速鍵鍵盤)方案。電路板上使用 WCH 制的 CH552G 晶片,配合機械鍵與幾顆電阻電容就做成了一個超簡易的四鍵機械 macropad 後來在 hackaday 上分享後不知道為甚麼突然爆紅,收到很多留言,所以我就在想,說不定我可以用同一套零件的方案改用 grid layout 來做到數字鍵盤欸?所以我就來試試看了。 甚麼是 Grid Layout? 以 Arduino 的基本電路為例,通常要抓一個按鈕是不是被按下,一般都會對一個針腳進行讀取(e.g. digitalRead),但是在這樣的情況下,每一個按鈕就會需要用到一個 GPIO 針腳。如果我要做一個標準的 4 x 5 鍵數字鍵盤的話,我不就是需要用上 Arduino Mega 才行了嗎?所以這當然是不可行的。 但是如果你有留意到,外面很多 Arduino 按鈕的範例都是使用 Pull Low (下拉)和檢測針腳是不是 high (高電平)的方法來實現的。因此如果我們有辮法在需要讀到某個按鈕的時候的狀態的時候才對它提供高電平,而沒在讀取的時候提供低電平(即使按下也不會產生高電平狀態)的話,那樣我們就能用一只 GPIO 來選擇讀取哪個按鈕,另一只 GPIO 來讀取選擇了的按鈕來增加總讀取的按鈕數量。 於是就出現了 grid layout 跟 line scanning 的方法了。簡單來說就是對於一個 4 x 5 的 grid 而言,我們只需要控制 5 個針腳以選擇哪一行 (row),然後 4 個針腳來讀取哪一列 (column) 就好。以這樣的方法,原本會需要用到 20 個針腳的電路,現在用 4 + 5 = 9 個針腳就能讀完了。 可是,這樣會出現一個問題。在小量生產 PCBA 的情況下,每一顆零件和焊接費用都是滿貴的。而這個 layout 會讓 10K 電阻比原先的 BOM (四顆)多了一顆!? 不過解決方法也很簡單,只要把電路反過來,使用 pull HIGH + LOW activate 的設計(即是說按鈕按下的時候 MCU 是讀取到 LOW / 0 而不是 HIGH /1 ),那電路的設計就變成下面的樣子,把電阻搬到 INPUT line 上面,從而省下一顆電阻( 設計好了之後就是電路板設計了。這裡用的是 EasyEDA,根據上面的原理大致畫一畫之後電路板就出來了。 正面(預留了 numpad + macropad 兩者的插孔) 背面 組裝的部分也是沒甚麼特別的,先把 micro USB 和 CH552G 焊上去之後,再焊一些比較大的零部件,最後就是燒錄程序作測試。 背面電路板,原型機板本背面甚麼都沒有印 正面裝上 keycap 後的效果,由於是開發軟體用的原型機所以用上了最便宜的量產通用型鍵帽 測試用的 Arduino 寫的 Ch552 numpad firmware,需要先灌 CH55X Arduino 燒錄器 外殼設計與打印 由於這片電路板背面也有零件,因此沒辦法馬上使用,必須要先把外殼生出來才能安心放桌面上使用(不然把零件撞掉就麻煩了),這裡使用 Aprint Editor 簡單的為這台東西設計個外殼 因為不知道要傾斜角度多少會比較舒適,因此設計成可以拆卸的結構以方便測試 原型機成品 由於這是原型機,主要作為軟體開發和測試用,因此外殼甚麼的都是拿手邊剛好有的材料來印(黑色 PLA)。但是外型差不多就是這樣。 正面 底部,兩則的是防滑膠條;用戶可以在那個洞黏上金屬板以增加重量 Micro USB 與 firmware program 的按鈕有一個凹位方便開發者使用 後方的 IO 就只有 micro USB 口與兩顆 LED 最終(小)量(生)產的版本應該會好看很多,但是功能上這東西的確比我想像中的要好(這篇文章就是用這個鍵盤寫的),如果加上配重塊與專用鍵帽的話我猜真的可以取代現在我在用的數字鍵盤的樣子(?
目前第 1 頁,共有 10 頁