使用 Apache2 當 Reverse Proxy 伺服器
因為最近房東在沒經我同意之前在房間的網絡上遊加入了一台 NAT 路由器,所以原本架在我的房間裡的 ArozOS 伺服器就沒辦法在外面連線了。想了想,於是我想到了可以透過 zeroTier 和 Reverse Proxy 來使用我房間裡的伺服器,所以便開始研究怎樣可以弄到一台 Reverse Proxy 的伺服器。 網絡上很多人喜歡用 nginx 來當 RP伺服器,可是基於某些原因我並沒有太喜歡它所以我就選擇用 Apache2 了。首先,安裝 apache2 sudo apt-get update sudo apt-get install apache2 -y 然後就是編輯它的設定檔,加入你需要 proxy 的目標 sudo nano /etc/apache2/sites-available/000-default.conf NameVirtualHost *:80 <VirtualHost *:80> ServerName ixtw.hkwtc.com ProxyPass / http://{zerotier 的區網 ip}:8080/ ProxyPassReverse / http://{zerotier 的區網 ip}:8080/ </VirtualHost> <VirtualHost *:80> DocumentRoot /var/www/html </VirtualHost> 然後 Enable Proxy 插件並重啟 apache 2 sudo a2enmod proxy_http sudo systemctl restart apache2 可是,WebSocket 要求過不去欸? 這是因為要 proxy websocket 會需要額外的 module 去處理,首先啟用 wstunnel 跟 rewrite engine sudo a2enmod proxy_wstunnel sudo a2enmod rewrite sudo systemctl restart apache2 然後加入 RewriteCond 跟 RewriteRule <VirtualHost *:80> ServerName ixtw.hkwtc.org RewriteEngine On RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC,OR] RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC] RewriteRule /(.*) ws://192.168.196.15:8082/$1 [P,QSA,L] ProxyPass / http://192.168.196.15:8082/ ProxyPassReverse / http://192.168.196.15:8082/ </VirtualHost> 最後再重啟一次 apache sudo systemctl restart apache2 這樣就完成了!
透過 Arduino 取得 UART HMI 螢幕頁面 ID 然後按需求轉頁
最近因為我在弄一個 60W 的 PD 充電器,然後想弄一個迷你的螢幕來顯示電池的資訊,所以我便選用了一個 2.2寸的 UART HMI 了。 2.2 寸的 UART HMI,比想像中要小一點 選 UART HMI 的原因有很多,網上也有很多文章教你怎樣選合適的螢幕,所以我就不細說了。簡單來說, UART HMI 是一種可以透過 Serial 來控制螢幕載入預先設計好的界面的一種人機界面。 發送指令到螢幕 HMISerial 是 Software Serial。以下 function 把 cmd 內的內容發到螢幕,如果 debugMode 被啟用,側會同步輸出到 hardware serial 上。 SoftwareSerial HMISerial(2, 3); // RX, TX //... void sendCommand(String cmd){ if (debugMode){ //Mirror output to serial Serial.print(cmd); Serial.write(0XFF); Serial.write(0XFF); Serial.write(0XFF); } HMISerial.print(cmd); HMISerial.write(0XFF); HMISerial.write(0XFF); HMISerial.write(0XFF); delay(50); } 使用例子: sendCommand("t0.txt=\"[info] MCU Connected\""); 取得現在 HMI 屏幕正在顯示的 Page ID 如果你把 sendme 指令發到屏幕,屏幕會回傳現在的 page ID 給你,它的回傳信號大約長這樣 66 01 FF FF FF 66 是這個指令的回傳碼,01 是現在的 page ID (即是 page 1),FF FF FF 側是傳送完成的意思,所以我們只需要在 Serial.read() 的時候抓到 0x66 就知道下一個一定是 page ID 了。 int getPageNumber(){ sendCommand("sendme"); bool nextReadIsPageNumber = false; while (HMISerial.available() > 0) { // read the incoming byte: incomingByte = HMISerial.read(); if (nextReadIsPageNumber){ //這是 page ID nextReadIsPageNumber = false; return incomingByte; } if (incomingByte == 0x66){ //下一個出現的 byte 就是 page ID 了 nextReadIsPageNumber = true; } } } 使用例子(檢查現在是否在 page 0(hex: 0x00)) currentPageNumber = getPageNumber(); if (currentPageNumber == 0x00){ //Do something } 成果(Arduino 透過 COM port 輸入到模擬器)
KIOXIA 32GB EXCERIA Micro SD 卡跑分
嗯,就大概這樣… 嘛,對於一張 36HKD 的卡來說不能要求太高吧(?)
3D 打印新手懶人包
見間唔中都有人會入坑試下玩 3D 打印,作為已經玩咗 3D 打印 4 - 5 年嘅 Maker,我就係呢到寫個懶人包方便大家搵野。有問題或者補充歡迎下面留言多多指教 【家用 3D 打印機分類】 3D Printer 有好多種,一般最多人用嘅家用機係 FDM 同 DLP ,以下係一啲你可能聽過嘅例子: FDM Puras i3、Tiny Boy (港產)、P 仔(港產)、Creality Ender 系列、AnyCubic Mega 系列 DLP AnyCubic Photon / NOVA、Creality LD 而你可能會以為佢係 DLP 但係其實佢係 SLA 嘅 FORMLABS Form 系列就唔係包括係上面個表入面 【揀邊種機好?】 你要考慮幾點 你打算印啲咩(結構 / 工程件?機械類模型?公仔?)你需要幾精細 / 想印幾快?你 budget 幾多? 基本上就可以大概知道你應該買邊種機。總括黎講,如果你: 只係打算印啲簡單模型表面光唔光滑唔緊要,最緊要硬正同埋印得快(整下一啲外殼、支撐或者功能性嘅野)想印完即刻用得,唔洗過水、等佢固化 你就可以去買 FDM 嘅機型;但係如果你 打算印一啲好精細嘅模型表面一定要光滑,打印件唔諗住會食力你屋企有個空氣流通嘅位置放部機唔介意印完之後要過水 / 用酒精處理、再放入 UV 燈箱照光做固化 你可以去買 DLP / SLA 嘅機型。 【要用咩軟件?】 3D 打印個過程大概係咁: 你用建模軟件畫一個 3D 模型你將個模型匯出到合適嘅格式(stl / obj)匯入去切片軟件匯出一個部 3D 打印機食到嘅指令檔(gcode / x3g)將個 file 放入去 SD 卡 / 電腦用 USB 線連接部 3D printer 開始打印 所以,係一般情況之下你會學用到兩套軟件(除咗一啲有 build in slicing 嘅 3D modeling engine,呢個後話)。如果你想學,下面呢個 list 你可以選擇一個適合自己嘅試下用。(帶 ✨ 號係香港本地嘅 startup 公司 / Makers 開發嘅產品 / 服務) 【建模 軟件】 帶 🔼 號係指呢個模型係以2D extrusion 變 3D 嘅模式建模,因為要經常 2D 3D 轉黎轉去,一開始唔習慣可能會成日畫錯野 / 搞錯方向〔新手向〕 Draw23D (小學 - 初中,用作將圖片變成 2.5D 的網上工具) ✨ http://www.draw23d.com aPrint Editor (初中 - 高中,帶雲端切片的 3D 建模工具) ✨ https://aprint.io/editor/ TinkerCAD (小學 - 初中,但無法直接匯出 Gcode,需要使用其他切片軟件切完個模型先印到)https://www.tinkercad.com/ Fusion 360 (高中或以上,比較完整嘅一個建模軟件)🔼 〔專業用〕Autodesk Inventor (有學生版免費)🔼 SketchUp (通常用黎畫建築圖)🔼 Blender (功能多到痴線,但係用得熟基本上乜都做到) ZBrush (畫公仔用,呢個我唔熟) 【切片軟件】 呢個通常跟機,特別係一啲用 custom protocol 嘅機型(例如 MakerBot 同埋 FlashForge),但係一般人嘅選擇通常係: 〔雲端切片〕 IceSL (一個超簡單的雲切片軟件)https://icesl.loria.fr/ aPrint Editor (係「列印」則欄有得直接 export Gcode 檔案) https://aprint.io/editor/ OctoPrint (需要自己買部 Pi 返黎…
Powerman v6 – 由 Micro USB 升級為 type C 的方法
話說我在一年前完成了一塊叫 Powerman v5r2 的一體式鋰電池充放管理板。它使用 IP5306 來對聚合物鋰電池進行 5V 2A 的充電和 5V 2A 升壓輸出。後來我也有放到 Tindie 上面賣,大約 65港幣一塊。 正面 背面 可是最近因為之前一次性生產的 100 塊存量即將見底,所以在準備下一批的生產過程。而在使用這充電板來做 STEM 套件的時候也收到不少的使用者意見,其中一個不時會聽到的是「不能用 type C 充電嗎?」 就是這樣,就讓我來設計一個能夠使用 type C 充電的設計吧! 要升級成 type C 聽上去很簡單,可是 Raspberry Pi 3 在升級到 4 的 type C 的時候出現過很嚴重的問題(有的甚至把充電器燒壞),所以我們就來看看當時 Pi 4 的初版是怎樣設計的: 這是 Raspberry Pi 4 初代的設計 你看到它 CC1 跟 CC2 是共用一對 5.1K 電阻,可是根據 USB C 的規格書,實際上的做法應該是這樣 由於我們不需要 5V 以上的電壓,我們只需要很簡單的用兩個 5.1K 電阻(Rd 位置) 分別連接 CC1 跟 CC2 再接地就好。 經過一大堆 re-route 之後,我的 Powerman v5 就成功升級成有 type C 的 powerman v6 了
Babylon.js ExtrudeShape 匯出成 STL 之後出現不正常 face data 的解決方案
在開發 aPrint 系統的時候,其中一個功能是使用 CSG 把兩個 Mesh 組合成一個容器。可是在組合匯出之後卻出現 face data 重疊的情況(也有人會把它叫做 z-fighting 或是 invalid normal ) 在 Windows 的 3D viewer 中可以看到全部面都有一些問題 這個我研究了一段時間,發現這是跟 extrude 模型的時候的設定有關。一般來說,為了避免模型在背面出現破圖,所以在 render 的時候也會把 rendering face 設成雙面。以下為其中一個例子 BABYLON.MeshBuilder.ExtrudeShape("container-outerwall", { shape: externalBorder, path: [extrusionStart,extrusionEnd], cap: BABYLON.Mesh.CAP_ALL, sideOrientation: BABYLON.Mesh.DOUBLESIDE }, scene); 可是這樣在 CSG 處理的時候卻會出現問題,我懷疑是與 Mesh.CAPALL 的設定有關,可是我並沒有深入研究是不是 CAP_ALL 設定了之後就不能使用 DOUBLESIDE 的設定。 然而在把sideOrientation 改成 FONTSIDE 之後問題就解決了。 BABYLON.MeshBuilder.ExtrudeShape("container-outerwall", { shape: externalBorder, path: [extrusionStart,extrusionEnd], cap: BABYLON.Mesh.CAP_ALL, sideOrientation: BABYLON.Mesh.FRONTSIDE }, scene); 所以結論就是如果你要進行 CSG 操作的話 sideOrientation 不要設成 DOUBLESIDE 就對了。
ArOZ Portable 主板開發計劃
說到 ArOZ Portable 計劃,這是一台可以放進口袋裡的迷你 ArozOS 伺服器。如果你沒看過的話現在的版本大約長這樣 最穩定版本的設計 可是如果你有機會把它拆開來看,你就會發現這版本其實是沒辦法量產的。因為裡面的線路長這樣 所以結論是:在真的量產之前我要先把它變成可以量產的設計。 ArOZ Portable 主板 通常說到電腦、手提電子裝置等等的東西第一個最基本的東西就是主板了。 這個計劃由 2016 年開始我就不斷在嘗試弄一塊主板來省略那堆亂七八糟的走線,可是每次都因為不同的原因而失敗。最大的原因可能是因為我還沒有焊接 小於 SOP 跟 0805 針腳的技術力吧,也有不少原因是當年的畫板技術還沒足夠讓我真的去自由發揮。 在這裡我就跟大家分享一下這幾年來我設計的 ArOZ Portable 主板(失敗品)吧 初代 ArOZ Portable 主板 第一代的 ArOZ Portable 主板 這一塊主板基本上就是一塊 FE1.1S USB Hub 晶片 + Micro SD 轉 USB 的晶片(用以代替外接的 USB 儲存裝置),但是當然這兩塊晶片的腳位都是 SSOP 的,而到現在我還是沒有合適的工具去焊接 SSOP 腳位,所以這板子當然是失敗了。 第二代主板 第二代的主板主要是把 SD 卡讀卡器移除,跟把充電電路放回進去。你在 U2 的位置能夠看到一個熟悉的 TP4056 layout,理論上是可以提供 5V 1A 的充電性能。但是唯一一個問題是這電路設計的時候沒考慮到 5V 升壓問題(而且還要畫完,在掉去生產前一刻才發現這問題),而大部分 USB 裝置必須要有 5V 供電才能運作,結果這板子也是不能用的。 第三代主板 在發現第二代這種低級錯誤之後第三代就換回去用移充的主板來供電了(右上的那個凹進去的位置),然後順便加粗了供電的走線和買了焊膏和熱風槍。雖然有成功焊出一兩塊,但是最後也是因為 FE1.1S 焊的良率太低而報廢(果然還是需要紅外線焗爐啊) 第四代 想著既然第三代都能有幾個 % 的成功率,那第四代也能碰碰運氣看看定 20塊板子有沒有一塊能成功吧?於是我把充電、升壓、USB Hub 等等全部放到一塊板子上面,結果良率太低全部焊完之後一塊正常運作的板子都沒有(# 第五代 「算了,還是 back to basic 好了。」用這種邏輯而畫的版子。只提供了最基本的電源、USB port 信號 routing 跟 USB Hub 的晶片,結果雖然有一點問題,但是加一點 patch wire 之後總比一大堆線要好。可是這體積,應該還能再小一點? 第六代 因為考慮到能不能把良率超低的 FE1.1S 找 OEM 進行焊接,然後以模組的形式插回到主板上而開發的板子。基本上與第五代的設計一樣,就是把 FE1.1S 的 footprint 換成了 2.54 針腳的插槽 可是後來這版本並沒有進行生產,因為問了才知道這麼少的生產量走一趟 OEM 貼片實在太貴了(比起買 USB hub 模組回來拆還要貴),所以就這樣這東西就放置了 1年多(由 fyp 開始到 grad 我都沒碰過這計劃) 第七代 在香港疫情爆得最厲害的時候因為在家裡沒事幹所以抽了點時間來設計這塊板子。這基本上是基於第七代的設計,但是把多餘的板子部分切走,讓它最小化方便安裝。可是沒進行生產的原因也是跟第七代一樣。另外你也能看到背面原本要放移充主板的位置被一塊 Powermanv5 代替。 主板背面,使用自家開發的 Powermanv5r2 主板代替移充主板,這樣就不用每做一台 ArOZ Portable 就犧牲一塊移充 背面用來供電跟充電的 Powermanv5r2,也是自家開發的其中一種通用模組,已經量產 https://www.tindie.com/products/tobychui/powerman-power-boost-lipo-charger-inoutput-5v-2a/ 第八代 經過這一年來不斷來回生產不同的 PCB 來做 STEM Kit 跟 了解了更多不同類型的晶片供應商,現在終於有技術可以重新回到這個 project 了。在幾個巧合的情況下才讓我有辦法設計到這塊板子: 在 twitter 看到有工程師分享一塊 SOP16 的 USB Hub 晶片:SL2.1A在拆移充時發現了一塊更便宜的充放管理晶片:HT4928我已經開發好 Powerman 跟 Powerloli 兩塊通用鋰電池充放管理板(內置升壓功能)終於理解 When in doubt, ground it out (有懷疑不知道它要連哪裡的時候,就把它接地吧)的道理 後面可以直接拉線到 Raspberry Pi Zero W 的 test pad 供電和 USB 接口 然後就是等 Power Loli v2 的樣品 PCB 回來,測試過沒問題之後就可以正式生產這塊第 八代的板子了。 8 May…
Refused to execute script from ‘xxx’ because its MIME type (‘text/plain’) is not executable.
Go 在 Windows 10 上使用 File Server 傳送 JavaScript 的時候間中會出現這個錯誤,網上的其中一個解決方法是更改系統 register 讓 Windows 把 JavaScript 辨認為 text/javascript。然而如果你不想透過更改系統設定的方法解決的話你也可以透過編程方法解決。 使用 Router 來手動設定正確的 Mime Type func mrouter(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if filepath.Ext(r.RequestURI) == ".js" { //Requesting a js file w.Header().Add("Content-Type", "text/javascript") h.ServeHTTP(w, r) } else { h.ServeHTTP(w, r) } }) } 使用方法如下 fs := http.FileServer(http.Dir("./web")) http.Handle("/", mrouter(fs)) 這樣便修好了
淺談雲端文化及 Vendor Lock-in 問題
首先,現在很多學生跟客戶都已經懂得「雲端」/ Cloud 這個概念。要知道其實 Cloud 並不是甚麼新奇的東西,也不是這近 10 年才出現的東西,這個(類似的)系統 60 年代就已經存在的了,只不過當時我們叫它做「Client Server Model」而已。至於 Cloud,與其說它是一個技術,我反而覺得它比較像一個 Marketing 用的術語,對於要用在 technical document 裡面,我就比較有保留。 甚麼是雲端 / Cloud 我對雲端的見解基本上就是 別人的電腦托比 這個我也曾在 Hong Kong Open Source Conference 2020 說過這個(不過是比較以開玩笑的角度來說),而實際上,如果你真的要向一個完全不理解雲端技術的人說明到底雲端的東西存放在哪裡,基本上你也會回答出類似的答案。 source: https://xkcd.com/908/ 現代的網絡與雲端 很多時候現在的開發者對現代的網頁系統架設都是有以下幾種想法 是一個 Web Hosting Service 架設的網站,我只要在他們的伺服器上上載我的網頁檔案即可(例如 Github page 或是一些免費的 web hosting 網站)直接在網頁上編輯網頁的供應商(例如 wix / wordpress)一台電腦,上面開著 XAMPP / WAMP / LAMP 來架設網站(例如自己寫的 php / HTML 網站)一個 process 開著一個程式以處理要求(e.g. Nodejs / Golang) 可是實際上現代的網頁系統比你想像中的要複雜一點,基本上會自己架伺服器的已經不多了,還會用上很多第三方的 cdn 檔案等等。但是由於這篇文章不是用來教你設立網站,所以我直接跳到結論: 現代的網頁應用程式對於第三方依賴性比以前高很多 例子好像說 Cloudflare 、AWS / Google / Azure 的 VM 、CDN、Github / Gitlab 等等 成本與依賴性 通常我說依賴性,我指的有兩種,一種是開發的依賴性和運作的依賴性。如果你有寫過 Nodejs 或是 Golang 的網頁你應該都有聽過 module 這東西,這東西就是我所說的開發依賴性之一;另外一種就是運作的依賴性,例如說 cdn、api 之類的東西。 我對第三方依賴性其實沒有甚麼特別抗拒,畢竟你還是需要把你的系統跟其他的網頁系統連接在一起。例如分享到社交平台、電郵等等。然而,有一些東西是我絕對不會碰的,就是具有平台依賴性的 API。 甚麼是平台依賴性? 平台依賴性是指你寫的程式必須要使用某一供應商的平台才能正常運作。例子如 Firebase、Lambda 等等。 為甚麼具有平台依賴性的 API 應該盡量避免? 平台依賴性的 API 會讓你無法轉移系統到另一平台,做成 Vendor lock-in 的情況。這情況除了網絡之外其他平台也會出現,例如說必須依賴 Windows API 的(古老)程式、必須依賴 iPhone + iOS 的硬體才能運作的 App 等等,這些都是 vendor lock-in 的例子。 你可能會覺得:這個我只是用來試試市場或是所謂的 Proof of Concept 作品而已。用這些 API 也沒甚麼吧? 不對,因為很多時候商業機構看到你的 PoC 作品能用就會直接在上面加建成為他們的最終產品,然後繼續用它來發展商業項目。這個時候就會變成真正的 Vendor Lock-in 了。如果有一天供應商中止他們的服務或是突然收取很高昂的平台使用費,到時候你就沒辦法只好乖乖的付錢了。 所以我一向的做法都是當有客戶要選擇使用雲端服務建立網頁系統的時候我都會推薦他們用 VM。VM 的 Guest OS 我一向都會選擇開源的 Debian 或是 Ubuntu。這樣萬一有需要改換供應商的一天,整套系統也可以很輕易的從一個 Linux VM 轉移到另一個 Linux VM 而不需要更新原本的 code base。 可是如果不依賴第三方 API 有這麼多好處,為何這麼多人還是在用具平台依賴性的 API? 最主要還是成本問題。這些 API 的供應商一般都會做出簡單易用的網頁界面,讓網頁系統的開發者很容易就可以透過 API KEY 來存取到他們在雲端上的數據庫、core logic 或是其他本來需要伺服器運算的東西。他們能夠省下更多開發及架設後端的時間,更不用對著 Linux terminal 輸入指令還要處理間中跳出來又看不懂的 linux 錯誤。在以 man-hour 收費的 IT 界來說,這種能夠節省成本、客人又看不出來而且又省功夫的做法,誰不想用? 一分錢一分貨 很多人以為軟體跟去超市買菜一樣,能叫價就叫價、去比較便宜的那家買就好。其實與其說 Software 是一件商品,你應該視它為一件藝術品。再怎樣簡單的程式也能夠有便宜和貴價之分別,如果你沒有 technical 背景可能看不出來,但是很多時候這些價錢差就差在 有沒有使用說明及技術文件有沒有處理 Edge Case處理速度、穩定性界面的人性化設計、Flow 設計跟使用者體驗兼容性、平台依賴性開發者溝通能力 所以說,開發軟體真的是一分錢一分貨,貪小便宜只會麻煩到未來的自己。 實用與理想之差別…
Sinilink XY-WF5V 灌 Home Dynamic v2 IoT 控制器
最近因為一些原因拿到了兩塊 Sinilink 的 XY-WF5V WiFi 繼電器模組。由於它的內置 firmware WiFi 有鎖,加上它的 App 看上去就覺得很爛,所以我決寫把它的 firmware 刷掉重新寫一個 HDSv2 的 firmware 進去,讓我的雲端 IoT 控制器能夠控制到它。 首先,怎樣在非開發板上灌程式? 這個時候你就需要 USB 編程器了(大陸叫下載器,通常我叫它 ISP),你可以用不同的方式來灌程式進去,可是我這裡用的是最便宜的 ESP01 用編程器,樣子大約長這樣 然後我們就可以直接把線從那個 2 x 8 2.54 母頭裡拉出來,直接焊接在 ESP8266 上面,線路如下 接線圖 完成之後大約長這樣 逆向工程 之後就是把板子的線路重新畫出來。由於這是一塊兩層板,所以沒甚麼大問題。之後再把找到的 GPIO 跟據需要重新寫進 HDSv2 On/Off 的 example sketch 裡面, 可是,我們怎知道應該使用哪一塊開發板的配置呢? 這個我們可以選擇使用 Wemos D1 R1 作為基礎,重新把 ESP8266 上對應的針腳在 Wemos D1 上找到對應的 Digital Pin 號碼即可。完成之後 Arduino sketch 裡面的開關 pin 被改成如下: int signalOutputPin = D14; //The pin to activate the mosfet of relay int buttonPin = D6; int refPin = D8; 如果找不到 Pull up / down 電阻怎辦? 另外,如果你在板子上找到沒有 pull up / pull down 的按鈕,很有可能它是使用軟體方式做的 internal pull up / down 電阻。在更改 HDSv2 的 example 時只要把 INPUT pin 的設定由 INPUT 改成 INPUT_PULLUP 即可,例子如下: pinMode(buttonPin, INPUT_PULLUP); 相關可以使用程式 pull up / down 的針腳可以參考下表(請對應 Generic 8266 的 GPIO 號碼,不要使用最左欄的 pin 號碼) 完成圖 完成之後在 ArozOS 內重新掃描一下 IoT 裝置,應該就會有新的 Sinilink WiFi Relay 在列表中出現了。
目前第 4 頁,共有 11 頁