2026年1月3日

雜談: AI Fine-tuning之我見,為何要Fine-tuning AI模型?

這是一篇雜談,自以為是的認知。😂

我先前在survey gemma3:270m fine-tuning時才發現,gemma3是不支援MCP的,而Twinkle AI的成果有把MCP功能,也就是function call的能力加進去。

於是我就疑惑了,看了才發現AI的秘密 - Tokenizer,也可算是Embedding模型,它作用是分詞,把整句話切成一個個單字、名詞、動詞。

LLM起手式第一動就是分詞模型分詞,這個分詞的詞就是Token,然後根據模型把每個Token的結果回覆。 簡單說,「這裡有一隻貓。」,經過Tokenizer之後,會切成「這裡」「有」「一隻」「貓」「。」,這一個個詞,會轉換成數字(句點會變成<end_oooooo>,這樣的特殊tag😁),稱為「Token」。

MCP的核心其實是function call,而function是以tag(<function>)的形式加在資料集裡面訓練的,沒有這個定義,AI不會知道要呼叫工具。 

白話說,<func>要定義在Tokenizer,要描述在Training Datasets,兩者都有,訓練後AI才會用工具。 今天的慶生會詢問後證實了,<function>無法透過RAG讓AI模型了解,只能透過fine-tuning在訓練集和Tokenizer加入。

例如:
Tokenizer 裡面定義 <func> = 呼叫工具 DataSet 裡面有一筆「問題: 請問台北的天氣如何?」「答案: 台北的天氣<func>。」

訓練後,當詢問時,如下:
「新竹現在天氣好不好?」
「天氣有<func>」=> 「是否允許呼叫 weather function 擷取天氣資訊? true」
「新竹的天氣,目前溫度58度,濕度125%」

最近LVA,機器人模型有些討論,但我們先不討論物理知識相關的訓練集處理,如何控制機器人? 機器人如何知道「往前走」=「A motor+B motor+C motor+D motor」的連續操作?是不是需要<function>。

非常多年前還在學校時,當時選修柔性計算有幾個機械系同學在做機器人,當時問他們,怎麼讓機器人動作? 它們跟我說: 「機器人所有關節都是伺服馬達,一個姿勢等於所有馬達的特定角度,可定義為一個frame,走的連續動作等於播放frames,像動畫片」 底層控制不會變,但LLM介入後,也許連續動作的控制能變成LLM自動生成。

那以前或者目前怎麼做? 常見的方式是人做動作後錄製,例如: 人彎腰,透過攝影機和關節姿勢擷取後,轉換成各個關節以及伺服馬達的角度,並且錄製。 接著讓機器人按照這些錄製的結果播放,因此很久前會看到機器人跌倒後,先定住,然後很制式的特定動作站起來。 這也是大陸很常看到的狀態,或者看到有人遙控機器人的情況。 Elon Musk機器人厲害的地方是,你看它動作行雲流水非常自然,這表示它各個關節的動作已經不是制式的play frames,而是根據動作,由LLM自動自然的生成,根據的不是單純的frames,而是有點像End-to-End的方式,讓AI直接理解行為,理解動作,把行為直接轉換成frame(motor控制動作)。

放了1個月沒提的知識點😁

結論:

訂閱AI重要,但AI fine-tuning模型裡面有些知識點,沒做過不會發現🤫  整理DataSets是苦工,而且不說沒人知道,說了也沒人知道能幹麻,它是「開發AI」的基礎,但不是AI應用的成果。 Fine-Tuning很重要,但參加活動問了人之後發現,幾乎都建議直接用RAG,我認真想過,也和AI討論過😂,目前Fine-Tuning在一般AI應用或開發中,成效有限,相同作用基本上都能在RAG中完成,除了Custom Tokenizer和Custom function之外。😂

發想:

基於貓砂機馬達控制FCM的延伸思考,結合function call和彈性化的增減function call工具的使用。 我在想是否根據特定情境修改和調整資料集,custom function tag,讓它能適應特定特殊應用。 而這點帶出2個延伸

一個是,以既有資料集修改增加tag以滿足特定問答時的特定工具應用。

一個是我在想的,用模板語言與特定語法進行Fine-Tuning,因為模板語言語意、語法和既有AI模型重疊度比較低,對於已有的模型影響也許比較小,那麼就能教會AI看特定的模板語言,就想像成我們用模板語言定義了瓦干答語,這個語言根本不存在,Fine-Tuning時等於讓AI學一個全新語言。 學會後,我們在RAG中根據瓦干答語~不是,是模板語言,寫出制式的語句和答案,那麼AI就能根據RAG查詢的的結果生成準確的答案。 這樣的應用,也許能用在機器人控制、End-To-End API、程式碼產生器,這些輸入或者輸出不是自然語言,或者用自然語言查詢(輸入)得出制式格式資料(輸出)的情境。

這應該能讓Fine-Tuning這件事變得有價值,如果輔以RAG進行模板語言查詢,應該能同時做到彈性和準確性兼備。 如果真如想像,那麼能帶出2點: 1. 地端或Edge端模型能和雲端模型搭配,Edge端負責控制,雲端負責行為判斷,例如,「小呆同學,我要喝水」,雲端AI能給出「水在水壺,水壺在廚房,到廚房拿杯子裝好水之後,拿到房間」,Edge端能給出「我控制腳步移動到廚房」「我控制手部抓取水杯」「我控制水壺倒水到水杯」「我拿起水杯」「腳步移動到房間」 當然,也可以是End-To-End,直接一個Edge端AI模型內部直接轉出成這些動作控制

2. 關鍵點會需要Training Model,這無法透過RAG處理,因此需要AI公司因應需求Fine-Tuning模型,或者根據需求新增RAG資料,而RAG資料的原型是模板語言,語法與Tag則是Fine-Tuning而成,應該能保護住知識產權

以上都是嘴砲,function calling的Fine-Tuning有拿網路上資料Training,但成果不明 😂 發想還沒實做,它的起手勢,要先學Mustache。

雖然不願意承認,但「使用AI」比較容易,使用產品還是簡單些,要造輪子,太累了😂

2026年1月2日

跨平台SSH Tunnel GUI工具 - dot-gSTM

 前言:

Linux桌面環境中的SSH Tunnel GUI工具極少,以前找了一陣子後找到gSTM,但這套在2006-08-05就停更了,只支援到GTK2.0,gSTM-1.3.7幫它續命到GTK3.0,但也更新到2020-12-16。

很早前就想過重構gSTM,但它是GTK2.0或GTK3.0寫的,還用了GTK的glade GUI設計器,GTK的C Library與C的實做,讓gSTM的UI分佈在整個程式各個角落,glade GUI設計器現在幾乎失傳了,而且只支援GTK,這造成要重構它得要重畫整個界面,因此難度等同於重寫一套GUI應用程式。

有了AI,正好可以用AI嘗試重構既有程式,改用新的程式語言以及新的UI Framework重寫,又能讓AI分析既有程式的程式碼邏輯。

成果:

整份專案在
https://github.com/codyfu/dot-gstm

實做過程:

實做過程還算順利,目前覺得AvaloniaUI給AI實做還不錯,目前的盲點是,AvaloniaUI的Debug,還是它改了,我編譯我執行我測試,有問題截圖後貼給AI,流程比Web用playwright慢得多。
儘管如此,claude-code分析能力相當不錯,請它分析gSTM,問它底層呼叫ssh指令的程式碼位置和程式邏輯,大概有9成正確,讓它分析後,再要求它按照原始邏輯在.NET實做,基本上八成正確。
目前程式設計通常被推薦用async/await的非同步模式,但我要求.NET完全按照gSTM原始設計,使用執行緒+Process的方式實做,Queue的處理則要求使用mutex,初步看起來應該還ok。
AI用AvaloniaUI實做UI時,雖然它會說,跟原始的gSTM界面一致,但實際上根本就他自己亂想亂畫,要貼執行畫面給它,並且一來一回讓它修修修,才能改到和gSTM界面接近。

結論:

這次用AI重構了一個既有程式,一方面實現了願望,一方面也實測了AI重構既有軟體的能力和過程。
重構的對象是C + GTK Library,看起來效果還不錯。

2025年12月23日

P2P WebRTC檔案分享工具 - Anywhere Sender

前言:

一直以來都用SendAnywhere,但它現在Linux版好像有點問題,WebRTC一直不太會寫,正好拿AI練習,一方便讓AI幫忙寫WebRTC,另一方面嘗試用AI寫個包括前端、後端、應用程式的完整專案,看看實際運作會遇到哪些狀況。

程式取名為Anywhere Sender,希望能跟SendAnywhere一樣方便好用。😀

對了~除了手機版本與應用程式版本之外,還有CLI版本喔。

實做的時間比預期長,我本來還以為一週就能打完收工,沒想到修修改改修修改改弄了快3週,就算是現在我認為都還算是Beta版,WebRTC的部份看來還是得下去看過順過流程,看來AI開發這事,在除錯階段還是會花費很長時間。

成果:

整份專案在
https://gitlab.com/ycfunet/anywheresender

實做過程:

實做過程遇到不少問題,就算是目前Release,我認為都還需要優化,但已經玩了3週了,就先Release一個Beta版。
這邊會列出2種在開發和測試階段遇到的狀況,一種是純粹開發遇到的,一種是AI開發時會遇到的。

開發問題:
1. AvaloniaUI System Tray問題
System Tray在Windows和MacOS應該沒什麼問題,AvaloniaUI在Windows和MacOS的行為描述都還完整,但在Linux下面,我用的MATE Desktop就無法顯示,一查發現xembed和SNI的問題,光這個就花了些時間,AI在這時候帶來的好處是,寫測試程式很快,以往大概都要先花半天一天survey,survey包括測試程式要用的Library、SDK、原理,寫一個測試可能需要半天一天,然後第二個測試程式、第三個測試程式,這功能可能最快需要3天,AI寫測試,給它參考網站和技術文件,它就能寫,幾個小時就能寫出3~4個測試程式,整個問題排除只需要半天~一天,時間大幅壓縮。
測試時,問AI它會說可能是A, B, C, D什麼的,但其實都不對 😂
直接問它,那為何Electron正常,但AvaloniaUI不正常,AI就也沒答案,還是得survey,不過好處是,AI看程式碼很快,直接丟Electron程式碼和Qt程式碼給它,它就能從中給出它們寫了2種模式 😂

2. Web QR-Code掃描器元件
AI建議用Web的QR-Code掃描器Library,但用了之後,AI寫出遞迴地獄,造成QR-Code掃描器時好時壞,無法正常運作,而且怎麼改都改不正常,後來下去看QR-Code掃描器Library,自己先個Sample,讓AI看Sample改進去,遇到React還是改不成功,最後讓AI把Sample包成function,React直接呼叫function才改成功。
看來,AI在遇到複雜的程式碼狀態時,跟人一樣會混搖,老方法依然適用,讓AI寫Sample Code或人寫,然後包成API讓複雜程式碼呼叫。

3. Web的WebRTC與SIPSorcery的WebRTC相容問題
同樣是WebRTC,但SIPSorcery傳遞的預設參數和Web版的WebRTC不太一致,這應該正常,只要不同的SDK,很容易遇到通訊協定有些差異要調整的,這個顯然AI也會遇到,跟人Debug一樣,時間同樣無法變快,最後一樣是老方法,把SIPSorcery程式碼下載之後讓AI看,讓他自己分析裡面的參數缺少了什麼,才改出。優點是,AI看程式碼很快,給出明確方向,這種問題它能透過分析SIPSorcery、Send、Recv、Web WebRTC給出點和修改,這個人看的話,又要花好幾天,區別是,AI一開始會鬼打牆一直亂說找到問題點,一直亂改,直到你讓它停止,給它分析SIPSorcery和Web的WebRTC差異,它才找出問題點。

AI遇到的問題:
1. Web Framework框架不熟悉
一開始選用Konsta UI Svelte開發,結果寫超破,界面改不出,顯然AI還是要用React或Vue才能寫得好。

2. 檔案傳輸的緩存
AI寫檔案傳輸,預設就用個Web blob直接灌入整個檔案傳送和接收,這當然會造成大檔案傳輸會Memory不足或瀏覽器限制,但這個AI不會特別說,測試時被瀏覽器卡住了,才會說這是OOM(記憶體不足),顯然這不是正確方向,但它會給出建議縮小傳輸檔案大小。
我給AI要求解法,Web用Indexddb,Mobile用capacitor呼叫原生的FileSystem API,Desktop用檔案存取。

3. 兩端WebRTC檔案傳輸的訊息交換與非同步處理問題
這個其實還沒有完全解完,簡單的說,AI從Server、WebRTC傳送、接收的程式碼中,有可能還無法準確的把時序流程弄清楚,因此陸續改了一堆傳送端、接收端時序問題,像是傳送端丟完WebRTC斷線(接收端還沒收完)、Server信令交換在不同平台實做偏差(導致不同平台互傳漏流程,連線無法建立)、Web非同步動作造成時序的先後順序有問題。
目前我還沒下去完整看過WebRTC程式碼細節,但我認為目前Release 1.0.0版本,應該在Web UI Event(傳輸的狀態回傳)與WebRTC傳送、接收檔案的位置應該還有非同步或Event的問題,這可能是目前傳輸有時候會卡住或無法傳送的原因。

結論:

這次用AI實做了一個完整的專案,包括前、後端、手機、應用程式,速度快了不少,以往我不熟悉的知識領域(WebRTC、React、AvaloniaUI),透過AI能夠直接開發。
此外,以往工程師可能需要前端、後端、應用程式、手機,透過AI協作,可能真的減少為1~2位資深工程師就能處理。
這次開發的經驗,證實了網路流傳的說法,AI是資深工程師的開發加速器,但老實說對於新手工程師或中階工程師未必友好,因為它遇到問題時會鬼打牆,就算是Claude-Code這種等級,它不會在同一個點鬼打牆,卻會在沒答案的錯誤邏輯中鬼打牆出不來,此時需要經驗引導正確方向或作法,它才能改出,甚至在QR-Code掃描器的開發過程,程式碼複雜時,就算給出Sample Code,它還是會套用失敗,需要包成API用引用的方式才能改出,這表示工程師的經驗依然重要。

2025年11月30日

補作業之AI微調LLM - Gemma3:270m + 繁體中文支援

前言:

要學習微調LLM,那就跟隨前輩作業,試著將Gemma3:270m加上繁體中文支援。
https://www.facebook.com/groups/gaitech/posts/1461027975081413/

成果:

整份專案在
https://gitlab.com/ycfunet/mi50_training_test

實做過程:

我用MI50微調訓練,讓Claude Code實做微調程式,一開始直接用Transforms訓練,結果一直出現Grad norm = NaN,Grad norm = NaN並不是一開始出現,也不是固定位置出現,是隨機位置出現,可能80%出現,可能95%出現,都會讓訓練失敗,微調出的模型,回答要嘛沒內容,要嘛出現padding,或者常見的出現各國語言混雜一段一段的回答錯亂。
後來用unsloth之後,發現unsloth微調都能正常訓練完,微調出的模型,中文問答都能中文回答。
雖然還有些問題,但原則上都能正確回答。
另外,完整微調真的很花時間,MI50一張,500k的繁體中文資料集,需要連續60幾小時訓練。
unsloth的記憶體使用量比想像少很多,用unsloth訓練時,GPU VRAM是浮動的,但最多也就12%,用量還不到4GB顯卡記憶體。
花費2週,算是有點成果,細節熟悉度還不夠,但入門算有點點進展吧。

補充:
對了,先前用colab T4微調馬達狀態機,這次也移到MI50上,發現MI50訓練的時間,跟colab T4差不多,估計MI50和colab T4同等級。

2025年11月15日

AI協作MongoDB設計工具 - Mongo Modeler(Modified)

 前言:

在實做了dbdiagram-oss之後,如果是NoSQL像是MongoDB,有沒有類似的方式或工具呢?
我找到了Mongo Modeler(github),這個有團隊在開發和維護,而且一樣是純前端的工具,所以,應該應該可以無痛移植dbdiagram-oss後端吧。😁

以Mongo Modeler(github)為基礎用claude-code透過Vibe-Coding修改和實做,實做完成。

成果:

整份專案在
https://gitlab.com/ycfunet/mongo_modeler

實做過程:

它的後端幾乎不用改就能用,所以大部分都是前端修改工作。
考慮到Mongo Modeler還在持續維護和更新,我希望盡可能少的改動,Claude Code給了我實做建議 - JavaScript注入,非常聰明而有效。
因此,這個專案的前端改動,都是用外掛,以注入方式完成的,效果相當好。
但在Release前的測試時發現,MCP工具有點問題。😅
果然,問題總在意料外的地方出現。
MongoDB有定義Document的json結構,但這結構並沒有描述Collection之間的「關聯線(Relationships)」,但是Mongo Modeler是可以設定Collection間的關聯線(Relationships),並且能夠顯示,那它如何儲存的?
原理很簡單,Mongo Modeler有自己的json資料格式, Mongo Modeler資料格式中包括了Collection的座標以及關聯線(Relationships)描述。
但因為Mongo Modeler的json格式並不是標準,因此LLM不會有格式的資訊,LLM只認識MongoDB的Document json結構,這怎麼辦呢?
為此,我請Claude Code修改了MCP Server程式,先實做一個轉換程式碼,用來雙向轉換MongoDB ⮂ Mongo Modeler的json格式。接著想了想,讓它在get function同時回傳2種格式的內容,LLM就能透過回傳的內容知道2種格式的對應關係和Mongo Modeler格式樣式。接著修改create和modify,分別實做create和create_mongo以及modify和modify_mongo,這樣能夠讓LLM用這兩種格式建立和修改schema json,讓Ai能夠與Mongo Modeler協作。
Claue Code改的程式碼很厲害,它實做時強調,如果建立時是Mongo Modeler格式,修改時用modify_mongo,儘管修改時的關聯線(Relationships)無法處理,但它能保持modify_mongo之前的所有關聯線(Relationships)。👍

2025年11月9日

AI協作資料庫圖表設計工具 - dbdiagram-oss

前言:

在接觸AI如claude-code和gemini-cli之後,注意到了和AI協作的特點,在協作的過程中,通常會需要把資料或資訊和AI互通,讓AI知道資訊,或者AI改了之後,讓我們知道AI更新了什麼或如何設計、修改的。
後端開發中,資料庫是很重要的部份,尤其是資料庫資料表與欄位設計,通常設計好資料表與欄位後,多半就開始刻API了,既然刻API都讓AI實做了,那資料庫的資料表、欄位也要和AI協作才會快速又清晰。
DBDiagram是一個非常方便的所見即所得(WYSIWYG)線上工具,透過DBML語法撰寫,就能即時顯示ER Model,讓資料庫的設計方便又清楚。
那麼
DBDiagram是否有開源版?DBDiagram能否跟AI協作?
我以dbdiagram-oss為基礎用gemini-cli和claude-code學習Vibe-Coding,效果相當不錯。

成果:

整份專案在
https://gitlab.com/ycfunet/dbdiagram_oss

實做過程含花絮(😂😭):

它的後端是用熟悉的.NET Core c#開發,開發過程中就調提示詞的規格,讓它API使用EndPoint語法,但使用傳統Controller的命名風格。
MCP Server的部份,可能比較新,AI寫不太好,寫的不能動,我下去先看SDK用法之後,用範例改出一版乾淨版本之後,再讓AI參考這個乾淨版本寫,然後再新增MCP Tool和API呼叫,才OK。
它前端用的是Vue 3 + Quasar Framework,Quasar Framework比較小眾,原則上讓AI自己分析後先自己改,改著改著卡住,我讓它把程式碼位置先給我,讓它分析程式碼執行流程,然後我下去看了下邏輯後,跟它說錯誤點,再改後改出。
之後前端加功能,就讓AI自己新增了,都沒什麼問題。
一開始我用gemini-cli開發,之後改用claude-code。
gemini-cli比較幽默,我都跟它說:「去吧~阿斯拉~」,它會回答我「好的,全速前進」。
But...網路上的事情我也遇到過了,一開始註冊不懂,訂閱gemini-cli也設定API Key,我以為兩個都掛在我同個身份和訂閱上,我用API Key執行gemini-cli,gemini-pro 2.5用超爽,然後帳單噴了NT $14000多😱。
產出最多的2天,一天NT $8000多,一天NT $3000多😭。
然後,我就訂閱claude了。🤣
我對AI Coding的心得是,高階訂閱得付,AI的反應時間、Coding能力、程式碼的理解能力都明顯不同等級。
gemini-pro 2.5這種等級讓它看專案程式碼後修改,它如果看不懂或改不出來,那非claude、gemini的AI大概也沒辦法,一般常聽到350b、400b這種AI,實測Coding是無法的,可能context不夠長,可能不夠聰明,光是改個東西都很可能會繞圈,這也是為何claude、gemini都沒說它們背後運作的是多大的模型🤔。

2025年11月1日

微調Gemma3:270M用於FSM(有限狀態機) - 以貓砂機馬達控制Lab為例

 前言:

幾年前曾將家裡的貓砂機用樹莓派改裝,按照原本的控制邏輯用Python實做,並用於貓砂機控制。
在survey Gemma3:270M這種微型LLM時提到,Gemma3:270M具有很強的指令跟隨能力,貓砂機的馬達控制流程本質上就是FSM(有限狀態機)系統,而FSM的狀態處理本質上就是查表法,那麼,將Gemma3:270M的指令以「目前狀態」和「觸發IO」代入,生成「下個狀態」,不就能把Gemma3:270M應用在FSM(有限狀態機)系統,好像不錯,練習微調LLM,容易入門。

實做:

讓claude-code分析之前那個貓砂機程式,讓它把馬達控制的流程看懂,然後生成一個(FSM)有限狀態機的狀態表,接著讓它分析Gemma3:270 Finetune程式,程式內它的DataSet(資料集)用的是ChatML的格式,讓claude-code把馬達控制的狀態表轉成ChatML格式。

修改Gemma3:270 Finetune,將資料改用ChatML格式的DataSet,然後用colab執行,因為貓砂機的FSM狀態表不多,claude-code生出整個DataSet只有70條,整個微調訓練時間大概10分鐘即可完成。

成果:

用Ollama載入後,用固定格式問它,得出固定格式結果,如圖:


輸入「current_state: IDLE」「trigger_event: CMD_START_CLEAN」,
固定得出「next_state: STATE1_FORWARD」IO「in1_in1: 0, in2_in1: 1」。

整個Lab順利又快速,證明微調微型模型,可用於FSM(有限狀態機)的系統控制,測試時,幾次輸入,看起來結果一致性非常非常高(沒遇過結果錯誤的)。

程式碼:

https://gitlab.com/ycfunet/gemma3-270m_finetune-fsmsys

2025年1月1日

2025-01-01新年文~這幾年的前端之旅

這幾年斷斷續續學習網站前後端,一開始選型時,考慮到Java的基礎,因此看了GWT,後來發現目前的網頁生態已經全面以W3C以及Mozilla/Google/Microsoft/Apple為主的廠商和瀏覽器壟斷,基本上就是JavaScript方案主導了。

接著以不用前端框架 手把手打造基礎SPA為基礎,用TypeScript自己刻了一個SPA框架,並使用。

但改著改著,用著用著始終不順手,總覺得JS和TS語法寫著很彆扭,IDE的語法提示和程式碼樣式無法和自己的SPA框架適配。

近年Webassembly出現,想著是否能以WASM為基礎將SPA框架改成WASM,以便於開發,但發現支援WASM的程式語言支援度都不太好,尤其是和JS融合與DOM介接這兩塊。

於是看了c#的Blazor Webassembly,發現各方面整合都相當不錯,直接是Webassembly,整合JS和DOM都相當完整、穩定,提供純前端(Blazor Webassembly)和前後端(Blazor Server)方案,還能和.NET MAUI整合,直接做到了像是Capacitor這樣的Mobile的Web App開發方案和Electron這樣的電腦桌面的Web應用程式方案。

新年目標,將Blazor Webassembly學起來之後,實做一個Site Project。

2023年6月28日

金融自動系統

 前言:

2014年時,曾以Java為基礎開發了股市自動交易系統 - JavaATS,後來不了了之。
最近以先前的架構為基礎重新開發,系統改以Python語言為基礎,以PostgreSQL和MQTT通訊協定為核心,重新設計成開發式架構。
因為系統無法達成自動交易的目標,因此命名為金融自動排程系統。
這篇會描述系統架構。

系統架構:

系統延續JavaATS的排程器核心,以Python重新實做。
因為科技的進步,原先Configure Manager使用的XML現在直接改用ini + PostgreSQL,原本定義在XML內的Task,都改定義到SQL中。
原先各元件之間,透過一個Socket Server交換資訊,同樣因為科技的進步,現在直接透過MQTT就能達成,Socket Server交換資訊時的資料格式問題,現在透過json能夠方便的進行資料交換。

TimerServer:

這是整個系統的核心,它以Crontab語法為基礎,在SQL中定義多個Task,TimerServer會以while loop方式每15秒撈取一次SQL內的Task List,並比對當下時間和Task Crontab時間,符合就執行。
執行時,透過Python的os.system(),以及定義在SQL內的Task欄位,能夠指定直譯器以及程式參數,例如:
python3 Downloader.py
ts-node-script stock_invest_downloader.ts
python3 dir_scanner.py /tmp/data/

Crontab格式定義如下:

延續JavaATS的TimerServer,Crontab的語法基本上能做到特定日期時間執行、週期執行、每星期特定天執行。

多機跨網路系統:

TimerServer啟動時包括一個config.ini檔案,裡面定義了SQL的帳號、密碼、DB,一個稱為MY_RUNNER_NAME的名稱定義。
而在資料庫的Task List中,每個Task都包含一個runner的欄位,同樣的,TimerServer在檢查Task List時,會比對Task的runner和自己的MY_RUNNER_NAME是否一致,或者runner是否為空,如果為空或者一致,則進一步比對時間是否符合,符合則執行。
上述的runner設計,讓TimerServer變成了多機跨網路的系統,可在多台PC甚至樹莓派上執行,每台電腦都透過SQL撈取Task List,比對後判斷是否是相對應的機器和相對應的時間,符合就會執行,達到多機器跨網路運作。

圖形化管理元件:

Python支援Qt,在Blog以前的文章中寫過多次Qt文章,這裡緬懷良葛格,以前開發Qt程式時,良葛格是少數中文的Qt教學,而且不同於書籍和官方教材,他的文章內容精準,看得出是學過寫過得人,才寫得出的教學文章,可惜他竟然走了。
透過PySide2,能夠方便的新增、修改、刪除Task List的Task。


模組化元件:

只有TimerServer定時執行顯然不夠,要能建構出金融自動系統,至少還需要歷史資料、證券公司API行情程式、證券公司API下單程式。

以歷史資料而言,現在能夠過Yahoo Finance API的Python Package,直接撈取歷史資料,後續透過Python PostreSQL Package就能將資料寫入SQL中。
加密貨幣?
加密貨幣交易所也是有提供API的,NodeJS?
前面提到,TimerServer可透過指定直譯器執行,不一定是Python,當然也可以是NodeJS,如前面範例提到的,ts-node-script就蠻方便的。

證券API串接:

我使用的證券提供c#的API,原則上是PC的下單應用程式將Component拆出來,透過c#呼叫的,因此,Windows是必要的。
目前我開了一個Windows VM,用Visual Studio 2015的c#開發2隻程式,一隻為即時價格註冊和擷取程式,一隻為下單程式,證券公司的API中,即時價格擷取是一個API,下單是一個API。
因為以前的經驗,為了縮短開發時間,方便未來證券公司API程式改版。
我是以證券公司提供的範例程式直接修改,將視窗Dialog隱藏,增加右下角System Notify,然後加上config.ini、MQTT的相關程式碼。
作用,
即時價格擷取:
能透過config.ini、SQL撈取要註冊取得即時價格的Symbol,當取得即時價格後,透過MQTT發送。

即時下單程式:
透過MQTT取得下單指令,進行下單,並透過MQTT、LineBot回報委託情況。

上述2隻程式開發後,連同API一起放在一個Windows VM,並排程開機、關機。


資料分析與技術分析:

嗯~這是個大哉問,如果有答案就不會寫這篇了~啊~不是。
這個以TimerServer為基礎的系統,能夠方便的加上各種分析模塊,我們以典型的技術分析當作例子:
寫一隻Python程式,內容是
每日從SQL中撈取歷史資料,透過Python的TALib計算均線(Simple MA),然後根據是否Cross,從MQTT或SQL上發出或寫入觸發信號。
當然,也可以是透過觸發信號直接透過MQTT發出下單指令。

來點更複雜的吧。
OK......

結論:

透過新版的金融自動(排程)系統,能夠透過開發各個模塊疊加,形成一個多功能跨網路的金融系統,因為以模塊化設計,各種功能都能實做,小到下載歷史資料,大到結合AI測試都能開展,而且透過SQL和MQTT,能夠讓各模塊交互作用。
TimerServer排程器是系統核心中的核心,所有的元件執行都透過TimerServer,透過runner欄位,能讓TimerServer做到多主機跨網路的能力。


2023年2月20日

BladeX(SpringCloud)介紹

前言

這一兩年因為工作需要接觸了基於BladeX的Spring Cloud,深刻體會到Spring Cloud的博大精深,因為這是中國公司以Spring Cloud集成的微服務架構系統,台灣知道的人比較少,這邊就介紹下這個大傢伙。
另外,前一篇介紹了我對JavaScript和NodeJS的看法,這篇也會帶入,既然NodeJS這麼好,BladeX適用情境在哪?

BladeX介紹
BladeX是由上海布雷德科技有限公司集成的Spring Cloud解決方案,產品稱為BladeX。
BladeX包括開源版和商業版,不用懷疑,大家都用商業版,開源版基本上只用做入門,事實上以商業版而言,它的價格不算貴,商業版授權大概台幣30,000左右,無使用限制、無授權期限制、無升級版本限制,基本上就是買一次,無限使用。
BladeX有2個版本,BladeX和BladeX Boot,兩個版本分別對應Spring Cloud和Spring Boot。
使用上,大部分使用者都使用BladeX,BladeX Boot版本用的人很少很少。

BladeX系統需求
BladeX的架構是所謂的微服務2.0。
BladeX要佈署和運行,至少需要 MySQL、Nacos、Redis,其系統需求,除去MySQL、Redis、Nacos這3台伺服器外,通常還需要3台每台至少8GB(建議16GB)記憶體的伺服器。

上述意思是,BladeX的運作環境,伺服器需求6台起跳。

Spring Cloud與BladeX架構介紹
以Web Service觀點看Spring Cloud,Spring Cloud是目前Web後端微服務2.0架構的主流。
以前以SOAP Protocol為基礎的Java 2 Enterprise Edition (J2EE),目前都改以Spring架構實做。
單從Protocol觀點而言,J2EE使用SOAP為基礎,SOAP以RPC + HTTP + XML組成,Spring則以(WebSocket) + REST API + HTTP + json組成。
既然對比的是J2EE,Spring Cloud的特點是在高穩定、模塊化、大架構、分散式的企業級應用情境。
Spring Cloud由許多輕量級組件組合而成,我們從下面這個架構圖看起:











這張架構圖算是比較簡潔的圖,先以這張圖描述服務註冊(Service Register)和服務發現(Service Discovery)。

Spring Cloud的架構中,以服務註冊(Service Register)和服務發現(Service Discovery)為基礎,在Spring Cloud中,一般用Eureka (BladeX中是Nacos)。
所謂的服務註冊(Service Register),是系統中所有要運行的微服務,在加入系統時,都要先透過Eureka進行註冊(Service Register),服務註冊後,系統內要呼叫和使用服務時,都可以透過Eureka的服務發現(Service Discovery),自動的找出相對應服務的微服務。
例如:
上圖中右側有個Auth Service,是使用者認證用的,要在上圖系統中使用Auth Service
首先,要先在Eureka (BladeX是Nacos)中先註冊Auth Service
使用時,要使用的程式要先詢問Eureka (BladeX是Nacos),Auth Service是誰?Eureka (BladeX是Nacos)會將Auth Service的IP和Port回傳給要使用的程式。
這個註冊和使用過程看起來有點複雜,在Spring Cloud中,呼叫、查詢和使用封裝在Feign Client API當中,透過呼叫和使用Feign Client API,它底層直接將上述行為處理完成。

在寫程式時,我們會需要全域變數紀錄和處理環境變數,並讓系統內所有程式都能存取,同樣的,Spring Cloud的微服務也需要存取共用的變數資料,在Spring Cloud中,並不將它稱為變數,而將它稱為Config或設置參數。
因為這些Config需要能夠共用,因此這些Config需要特定一個服務儲存和管理,它就是上述架構途中的Spring Cloud Config(BladeX中是Nacos)。
系統內所有的微服務,都能夠透過存取Spring Cloud Config(BladeX是Nacos),儲存和讀取Config。
例如:
微服務要能存取MySQL,通常會將MySQL的JDBC URL存放在Spring Cloud Config(BladeX是Nacos),使用時取出。

前面提到過,BladeX是Spring Cloud集成的解決方案,在BladeX當中,上述的服務註冊(Service Register)、服務發現(Service Discovery)和Config參數管理,都是透過Nacos管理的,而Nacos是阿里巴巴針對Spring Cloud開發的輕量級服務。

Spring Cloud中包含很多微服務,實際使用時,Client端通常不會直接存取微服務,Client端一般會透過Spring Cloud的API Gateway存取,在Spring Cloud中是ZUUL,而在BladeX中,它包含了一個稱為bladex gateway的微服務。
架構圖如下:

BladeX的bladex gateway和ZUUL作用相同。
所有Client都存取API Gateway,ZUUL和其他微服務的需求,則透過Feign Client API互相存取,中間存取的API,都是REST API,資料格式都是json。

BladeX(Spring Cloud)與php/express/django比較
典型的Web應用,通常由SQL(常見MySQL)、Apache、Web Backend、Web Frontend組成。
通常SQL一台,Apache和Web Backend一台,其他Client端都在系統外部。
這樣的應用,一般使用php/nodejs express/python django都能方便開發,經典的CRUD這類都足夠並方便使用,我自己估計,大概9成的應用都屬於此。
BladeX與Spring Cloud適用在企業的大型Web應用,它最基本需要穩定,還需要模組化,能夠以模組形式開發新服務,還需要具備分散式系統的橫向擴充性。
例如:
當希望有多個Client入口分散系統負載時,直接開多個API Gateway即可,只要確定Nacos內API Gateway有多個Instance,就能橫向擴展服務。

上述這些需求,會讓系統架構的複雜度變高,比較不適合直接用php/express/django開發,底層需要的API和SDK太多太雜。
舉個例子:
Nacos有NodeJS的API,透過呼叫Nacos API,NodeJS也能做到服務發現、服務註冊、Config存取,但這個只是Nacos API,並不是Feign Client API,實際在開發微服務時,需要大量使用Feign Client API存取其他微服務,光是要把NodeJS Nacos API包裝成Feign Client API,就要花費很多時間成本,而在BladeX中,Feign Client API只是其中一組常用的API,其他像是資料庫ORM的MyBatis API、加解密API...等,大概有10幾組API,不大可能全部用NodeJS重造輪子。

結論
BladeX是博大精深的微服務Web Framework,它集成了認證、會員管理、後台管理界面...等功能,它的系統集成了以Nacos為基礎的許多輕量級Spring Cloud組件服務,且能夠擴展支援包括Prometheus...等監控系統服務,承襲Spring Cloud的架構、模組化設計、橫向擴充能力、服務管理...等特點,讓大型Web應用的開發變得容易。
同時,BladeX與Spring Cloud這類Web Framework的設計目標,並不是使用在常見的小型Web應用,如果開發的目標項目是一般會員網站或功能網站,系統需求鎖定在2台以下的Web Server時,以express/django開發可能更為簡單方便。
(圖與排版後續補上)

JavaScript/NodeJS Web前端之我見

前言

這一兩年因為工作需要接觸了JavaScript/NodeJS與Web前端,也因此對所謂的Web前端有了更深刻的體會和理解。

這篇文章雖然標題寫的是JavaScript介紹,但後面會描述JavaScript和Web前端的不可替代性。

Web前後端概念

早期的Web並沒有區分前後端,典型的開發方式是先做個Web版型,然後Web內容替換asp/php/jsp寫的function和變數,瀏覽器載入時,Server會執行這些function,並將執行結果放入Web中提供給瀏覽器。

目前的Web開發,則多半會建議前後端分離,所謂的前後端分離,指的是後端一樣是asp/php/jsp,現在甚至可以是NodeJS Express/Python Django。

後端開發後,提供的是所謂的RESTFul API,一般簡稱REST API或API。

前端開發,則幾乎都是以JavaScript/TypeScript為主的Web App。

為什麼分成前後端?有沒有必要?能不能不學JavaScript/TypeScript而用Java/Python/C++開發?

這是我在接觸Web前端時的一系列疑問,而後得到了答案。

1. 為什麼分成前後端?有沒有必要?

Google多半會查到,現在的趨勢如此,專業分工,前後端架構...等,但WordPress沒有前後端分離,一樣有很大的使用群,那需求和原因在哪?

其實最關鍵的需求和原因,是多平台支援。

現在這個時代,手機佔了很大的使用群,手機又區分了Apple和Android,Apple要使用ObjectC/Swift開發,Android要使用Java/Kotlin開發,而傳統的PC和Web要支援嗎?PC可能要用c#開發,Web要使用JS開發。

為了能夠滿足多平台開發,典型前後端一體的開發方式,無法適用在這樣的情境中,因此,將後端改為REST API,前端各自使用HTTP API串接成了解決方案,而這點,才是前後端分離開發的真正原因。

換句話說,假設今天明確的需求是,我們只使用Web瀏覽器,並且不打算支援手機,那麼是否要前後端分離其實沒這麼重要。

2. 能不能不學JavaScript/TypeScript而用Java/Python/C++開發?

這個問題,很多後端想跨入前端開發的都會問,我一開始也是如此,事實上這有好處。

想想看,團隊成員都會Java,直接用Java開發前後端,人力能自由調配,這是很大的優勢。

但實際上呢?答案是不太行,原因很簡單。

目前所有的Web瀏覽器,核心都是JavaScript Engine,而目前Web瀏覽器,尤其是Chrome,基本上跨所有作業系統平台,Chrome地位等同以前的IE,幾乎是Web瀏覽器的代名詞,因為它原生支援的是JavaScript Engine,學習JavaScript這件事就避免不了。

那TypeScript和其他語言又如何呢?

目前TypeScript和其他語言,都是透過轉譯器的方式,將程式碼轉譯成JS後,在提供給Web瀏覽器執行的。

Web前端優勢

我們先帶個比較無關的東西,看一看Google AI的TensorFlow SDK"主要"支援哪些語言?

TensorFlow API Document

TensorFlow SDK支援4種語言,包括:

Python/JavaScript/C++/Java

其中有趣的是,Java點進去會發現,它都寫這個支援未來可能移除。

Python因為簡單易學,是目前的顯學,學校教的程式語言,都開始改教Python,以AI主軸在類神經網路設計/訓練/推論這幾個開發點,程式語言帶出的系統架構不是重點,程式執行速度也不是重點,簡單實做和修改類神經網路才是重點,因此以Python為主要語言是合理的。

C++的優勢除了程式執行速度快,上至伺服器服務和Web後端,下至MCU都能支援,最重要的是,C++編譯後,Binary反組譯比較難,原始程式碼隱蔽性好,是主要語言合理。

Java在程式語言開發中,仍舊有非常高的使用率,因此支援Java不意外,但它的開發便利性不如Python,程式碼沒有隱蔽性,不如C++,因此有支援但可能移除就不意外了。

那JavaScript為什麼變成主要支援的語言?

簡單的說,目前所有Web瀏覽器都只支援JavaScript,而目前所有作業系統平台都有Web瀏覽器,支援JavaScript,等於能讓AI應用從PC到手機全部支援。

意思是,JavaScript與Web瀏覽器,原生就具備跨平台能力,儘管在不同平台上的整合度沒有各平台原生語言強,但套用Java以前的標語:One language run anywhere 完全沒問題。

JavaScript與分散式特性

傳統系統開發時,為了最大化系統效能,通常會測試系統可支持的最大負載量,然後將最大負載量設為80%左右。

一般而言,不論是PC/IPC/開發板,效能越強單價越高,通常都希望盡可能的降低程式CPU和記憶體的使用量,拉高系統可負載量。

當我們代入Web前後端開發後,上述的最大負載量能直覺反應,它是屬於Web後端的服務,那前端呢?

Web前端通常運作在Web瀏覽器中,因此JavaScript和Web瀏覽器執行在Client端。

一個極端的說法,對於Web前端而言,後端只需要有個Web Server提供儲存空間,能讓Web瀏覽器下載HTML和JS,後續就能在Client端的瀏覽器中執行。

從這個觀點看JavaScript和Web前端,它是完美的分散式架構,所有程式都分散運行在全部的Client當中,不會佔用後端Server服務資源。

白話的說,如果盡可能在Web前端用JS開發和執行,能最大程度降低後端服務器的負載量,增加整個系統的使用效率。

而目前Web瀏覽器和JS SDK能夠支援的運算越來越多,已不是傳統的JS function只處理頁面顯示,像是TensorFlow能夠直接用Web瀏覽器在本地執行Ai程式,WebGL能夠直接在Web瀏覽器繪圖,瀏覽器內嵌的影片播放器能夠直接播放影片並進行H264/H265...等影像格式解碼,以往這些負載較大的程式,現在都能轉到Web前端運行,以分散式觀點而言,能很大程度的降低後端負載,增加系統負載量。

Web3.0與區塊鍊

區塊鍊細節這裡不提,一方面熟悉度沒這麼高,一方面是要描述Web3.0與分散式發展的觀點。

前面提到,從Web前後端觀點看JavaScript和Web瀏覽器的應用,JavaScript和Web形成了完美的分散式架構,有沒有一種可能,讓全部的Web瀏覽器之間互相連線,運行同一支JavaScript程式,變成一個全分散的執行環境和系統?

我認為,Web3.0提出的區塊鍊就是答案。

我們把記憶回到以前的P2P,包括eDonkey和BT,並以eDonkey設計思考。

eDonkey這類P2P的典型設計如下:

所有P2P Client都有檔案清單,所有資源都在P2P Client,eDonkey Server處理的,是紀錄和提供P2P Client的IP列表。

P2P Client啟動後,先連上eDonkey Server詢問最新的P2P Client列表,再跟自己的P2P Client列表比對,接著根據上傳或下載提供檔案(資源)清單,接著進行檔案(資源)上傳和下載。

Server主要服務是維護P2P Client清單。

BT則進一步透過DHT,讓P2P Client之間能夠互相連線取得這份清單,進一步降低Server的依賴性。

那麼,有沒有可能這樣的設計用JavaScript開發在Web瀏覽器上面執行?

我認為區塊鍊是這樣設計的變形。

典型的區塊鍊,每個Client(錢包)都以token形式表示,Client(錢包)中每個代幣(資源)也都以token表示,每個所謂的主鍊(P2P Network),所有的代幣(資源)都以linklist的形式串接,新的代幣(資源)要加入到主鍊(P2P Network),需要主鍊上超過一半以上的Client認可後,串入其中。

會發現,Server在其中的角色和傳統P2P網路的Server有一點相似,Server所謂的交易處理,可看做傳統P2P網路中,新增檔案(資源)時,通知各個P2P Client,進行資源清單同步的動作。

當然,在區塊鍊下,區塊鍊的Server的服務更多,區塊鍊錢包的功能是以錢包為主,沒有所謂的資源,但如果以P2P和Web瀏覽器的分散性思考,比較能夠體會坊間將區塊鍊介紹為Web3.0的原因。


結論

JavaScript從一開始就是Web瀏覽器主要的執行引擎,在目前的Web開發中,具有Web瀏覽器執行的不可取代性。

因為Web的普及,Web瀏覽器是很重要的跨平台環境,這讓JavaScript具有特殊性。

又因為Web瀏覽器是在Client端作業系統中運行,讓JavaScript具有分散性。

以前Java所謂的One language run anywhere,現在的作業系統中,Windows Linux PC預設都沒有Java,Apple iOS也沒有Java,只有Android還以JavaVM為基礎。

但看看現在的Web瀏覽器,PC/Android/Apple iOS,每個平台都有,JavaScript在這個時代成為了One language run anywhere的代表。

以此為基礎,NodeJS可單獨執行,並且有Express這樣的Framework,這讓JavaScript從Web瀏覽器前端跨入伺服器後端服務程式,回想一開始期望的,假如團隊中都是Java開發者,前端到後端都使用Java開發,人力調配的便利性,如果Java改成JavaScript和NodeJS呢?

(圖和排版後續補上)

2021年4月5日

RaspberryPi Pico FreeRTOS Timer用Java/Qt Timer Style實作

前言:

我將FreeRTOS的Timer封裝,使用方式和Java或Qt的用法相似,好方便使用。

用法:

1. 參考Thread用法實作Class
Runner *runnerA = new Runner(1);

2. 實作Timer物件
每個Timer和Object實作一個Timer物件
CTimer *timer1 = new CTimer("runnerA", runnerA);

3. 設定Timer
timer1->SetTimer(5, true);

參數1: 定時時間(單位是FreeRTOS的tick)
參數2: 是否循環觸發

4. 啟動Timer
timer1->Start();

程式碼:

https://gitlab.com/ycfunet/freertos_timer_template

 






RaspberryPi Pico FreeRTOS Task用Java/Qt Thread Style實作

前言:

我將FreeRTOS的Task用Thread方式實作,使用方式和Java或Qt的用法相似,好處是使用上比較方便習慣些。

用法:

1. 繼承使用Thread
#include "pico/stdlib.h"
#include "thread.h"

class LedWriter : public Thread
{
};

2. 實做run Method
public:
    LedWriter();
.....
    void run();
.....
3. 主程式中實做
TaskHandle_t ledwriter_thread = NULL;
LedWriter *led_writer = new LedWriter();

4. xTaskCreate啟動Task
BaseType_t ret = xTaskCreate(LedWriter::start, "led_writer", 512, led_writer, tskIDLE_PRIORITY, &ledwriter_thread);

程式碼下載:

https://gitlab.com/ycfunet/freertos_thread_template


2021年3月30日

RaspberryPi Pico FreeRTOS Debug測試

前言:

Microcontroller如果同時多個感測器,複雜度會提高,當複雜度比較高時,就需要分時多工,作法上通常是寫或套用個Scheduler,或者往上一階放個RTOS上去,放OS系統複雜度比較高,但提供的功能和彈性比較大。
RTOS當中,目前的主流大概是FreeRTOS,一方面它的Licence是MIT,另一方面它支援的Microcontroller比較多,Plugin也比較多,因此找Pico的FreeRTOS嘗試看看。
因為Microcontroller比較複雜,更需要UI的Debugger協助,除錯或開發才會比較有效率。

環境:

Host: Ubuntu 18.04
SWD Adapter: RaspberryPi 4b
Target: RaspberryPi Pico

安裝設置 (Host端):

Git下載
# cd /tmp
# git clone https://github.com/PicoCPP/RPI-pico-FreeRTOS
# cd RPI-pico-FreeRTOS
# git clone https://github.com/raspberrypi/pico-sdk
# git clone https://github.com/FreeRTOS/FreeRTOS-Kernel

QtCreator編譯FreeRTOS專案:

1. 開啟專案
用QtCreator開啟 /tmp/RPI-pico-FreeRTOS/CMakeLists.txt

2. 修改CMakeLists.txt新增Debug
.....
set(CMAKE_BUILD_TYPE Debug)
.....

3. 建置
Rescan Project
全部清除
清理專案 "hello_world"
全部建置

FreeRTOS除錯:





RaspberryPi Pico C/C++ Debug (QtCreator篇)

前言:

這邊延續前面CLI篇的說明,OpenOCD的設置已經做過了,這邊直接設定QtCreator。

環境:


QtCreator設定:

1. 參考下圖進入設置頁面

2. 設定Debugger
先設定ARM Debugger (需先安裝gdb-multiarch)



設置內容如下:
Name: ARM Debugger
Path: /usr/bin/gdb-multiarch

3. 設置Kits



設置內容如下:
名稱: Pico
裝置型態: 通用 Linux 裝置
Compiler: C:     GCC (C, arm 32bit in /usr/bin)
Compiler: C++: GCC (C++, arm 32bit in /usr/bin)
Debugger: ARM Debugger (前面Debugger設定的名稱)
Qt 版本: 無

4. 設置Linux裝置







裝置名稱: Pico SWD Adapter
Pi 4b SWD Adapter IP: 192.168.1.35
username: root
使用者密碼: root密碼 (Pi 4b要設定sshd_config,PermitRootLogin yes)

5. Kits設定Linux裝置為上面的Pico SWD Adapter





















開始Debug


Kits: Pico
Server Port: 3333
本地執行檔: 選取build路徑下的.elf檔案 (piblink.elf)
Break at "main": v



















RaspberryPi Pico C/C++ Debug (CLI篇)

前言:

RaspberryPi Pico有提供SWD界面(前身是JTAG),能透過SWD Adapter除錯。
身為老古董,以前用JTAG,需要一個JTAG Adapter,現在時代進步到SWD了,以前的JTAG Adapter同樣升級,現在是SWD Adapter,對老古董而言,特別的是以前JTAG Adapter比較貴,而且可能需要個ARM的程式提供Driver和Protocol,現在有OpenOCD,OpenOCD是OpenSource,而現在的RaspberryPi Zero/2b/3b/4b都能用GPIO模擬SWD Adapter,甚至RaspberryPi Pico也有picoprobe,直接放韌體,就能讓Pico當SWD Adapter。
因為是延續JTAG,因此現在主流的Microcontroller看起來都支援SWD,常見的除了新出的Pico之外,STM32也是常見的平台。

因為Debug有點複雜,因此分2篇,這篇做的是OpenOCD環境建置以及gdb指令除錯。
也許會說,那我直接跳下一篇,直接看QtCreator呢?
不太建議,因為設定和建置都在這篇,QtCreator的設定基礎,也都是gdb,這篇實做後,才能銜接上。
SWD Adapter的部份,這篇先用RaspberryPi 4b,原因是,OpenOCD RaspberryPi的設定,預設是RaspberryPi 4b,如果要用其他版本,要修改記憶體位址(memory address)、時脈(clock)、GPIO號碼(GPIO number)。

環境:

Host: Ubuntu 18.04
SWD Adapter: Raspberry Pi 4b
Target: RaspberryPi Pico

接線示意圖:






Pi OpenOCD環境安裝:

1. RaspberryPi OS安裝
先按照官方說明安裝Raspberry PI OS Lite
設置SSH
WiFi,我這邊設定WiFi為固定IP(192.168.1.35)

2. OpenOCD安裝
2a. 安裝套件
# apt-get update
# apt install git gdb-multiarch
# apt install automake autoconf build-essential texinfo libtool libftdi-dev libusb-1.0-0-dev

PS1. 下載OpenOCD Source Code要用git
PS2. Cross GDB執行檔現在直接包成gdb-multiarch套件,能直接支援Pico Binary format

2b. 下載OpenOCD
# git clone https://github.com/raspberrypi/openocd.git --recursive --branch rp2040 --depth=1

2c. 編譯 & 安裝OpenOCD
# cd openocd
# ./bootstrap
# ./configure --enable-ftdi --enable-sysfsgpio --enable-bcm2835gpio
# make -j8
# make install

2d. 安裝完成
安裝後檔案位置如下:
OpenOCD Execute file => /usr/local/bin/
OpenOCD Configure files => /usr/local/share/openocd/scripts/
RaspberryPi OpenOCD cfg => /usr/local/share/openocd/scripts/interface/raspberrypi-swd.cfg
RaspberryPi Pico(RP2040) Target cfg => /usr/local/share/openocd/scripts/target/rp2040.cfg

3. Host端GDB安裝

因為OpenOCD提供telnet、gdbserver、tcl除錯需要的3個界面,因此Host需要安裝:
# apt install gdb-multiarch telnet

4. Pico設置Debug模式
4a. 按下圖步驟將Pico上電



4b. Pi 4b執行OpenOCD
# openocd -f interface/raspberrypi-swd.cfg -f target/rp2040.cfg -c 'bindto 0.0.0.0'


PS1. OpenOCD預設binding在本機(localhost),要加上參數 'bindto 0.0.0.0'
PS2. 如果出現錯誤訊息,請確定是參照4a步驟,按著按鈕插USB Cable (上電)

5. Host上用gdb除錯

QtCreator會Build在不同目錄,專案目錄「/tmp/my_pico_cpp_test」,檔案會產生在「/tmp/build-my_pico_cpp_test-unknown-Default」。

根據CMakeLists.txt設定,Pico燒錄用的檔案是uf2,Debug用的檔案則是elf,因此這裡要使用的是「piblink.elf」。

Debug執行步驟如下:
5a. 執行gdb
# gdb-multiarch /tmp/build-my_pico_cpp_test-unknown-Default/piblink.elf

5b. 連線到OpenOCD並載入piblink.elf
(gdb) target remote 192.168.1.35:3333
(gdb) load




5c. 設置中斷點在main並執行
(gdb) b main
(gdb) continue






補充:

如果要使用Pi Zero (Pi 1)或者Pi 2b當作OpenOCD的SWD Adapter,OpenOCD的cfg檔案內容如下:
Pi 2b:(已測試)
# /usr/local/share/openocd/scripts/interface/rpi2.cfg
# Use RPI GPIO pins
adapter driver bcm2835gpio
bcm2835gpio_peripheral_base 0x3F000000

bcm2835gpio_speed_coeffs 146203 36

# SWD                swclk swdio
# Header pin numbers: 23 22
bcm2835gpio_swd_nums 11 25

transport select swd

adapter speed 1000

Pi Zero (Pi 1):
# /usr/local/share/openocd/scripts/interface/rpizero.cfg
# Use RPI GPIO pins
adapter driver bcm2835gpio
bcm2835gpio_peripheral_base 0x20000000

bcm2835gpio_speed_coeffs 113714 28

# SWD                swclk swdio
# Header pin numbers: 23 22
bcm2835gpio_swd_nums 11 25

transport select swd

adapter speed 1000


2021年3月27日

RaspberryPi Pico C++ IDE開發實做 (QtCreator)

前言:

RaspberryPi出Pico版本,開發比較接近Arduino,是non-OS的系統開發。
Pico提供C/C++和MicroPython開發方式。
因為我比較習慣用QtCreator開發C/C++程式,因此嘗試用QtCreator當IDE。

系統環境:

Ubuntu 18.04

套件安裝:

1. cmake安裝
先參考https://apt.kitware.com/透過下面步驟安裝最新版cmake。

1a. 新增kitware相關的key
# wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null

1b. 新增kitware ppa
# apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main'
# apt-get update

1c. 安裝cmake
# apt install cmake

2. 安裝QrCreator和Pico需要的Cross-Compiler
# apt install git qtcreator gcc-arm-none-eabi libnewlib-arm-none-eabi

3. QtCreator新增專案

3a. 開啟QtCreator

3b. 建立Non-Qt C++ Application專案




3c. 

3d. 選擇CMake

3e. 選擇預設Kit

3f. 目前不使用版本控制





4. 在目錄內git下載pico-sdk

$ cd /tmp/my_pico_cpp_test
$ git clone https://github.com/raspberrypi/pico-sdk
$ cd pico-sdk
$ git submodule update --init

5. 複製pico_sdk_import.cmake (參考pico-sdk github)

$ cd /tmp/my_pico_cpp_test
$ cp pico-sdk/external/pico_sdk_import.cmake ./

6. 修改CMakeLists.txt (參考pico-sdk github)


PS1: 專案內所有cpp檔案都加在 add_executable() 內
PS2: 主程式和產出的uf2名稱加在 add_executable()、target_link_libraries()、pico_add_extra_outputs() 三處內

7. QtCreator新增C++ Class

7a. 檔案 -> 新增檔案或專案 -> C++ -> C++ Class


7b. 設定LedWriter Class













8. 參考blink.c實做LED GPIO Output



9. 實做main.cpp

10. 建置專案

執行
Rescan Project
Clear CMake Configure
全部清理
清理專案
全部建置

11. 產出檔案


12. 燒錄檔案到RaspberryPi Pico

參考Pico官方文件
12a. Pico USB拔除
12b. 按著白色按鈕
12c. 插入Pico USB到電腦
12d. 跳出RPI-RP2 USB資料夾
12e. 把piblink.uf2 檔案複製到上述資料夾內
12f. Pico會自動Reset,並執行piblink.uf2



2021年3月23日

Linux Chroot實作 (Debian)

前言:

Linux Container(容器化)現在已經很常見了,但討論主要集中在Docker,Docker有非常多優勢,像是Docker Hub、Docker Volume、Docker Image與Container,進一步擴展還有K8s。
但Docker使用容量有點大,不論是Docker Image、Container,Docker相對有點點複雜,對於服務隔離而且硬體受限(低階VPS)的場景,找到Chroot,也就是Container的始祖。
相對於Container,Chroot比較少人討論,因為它僅有執行環境隔離,沒有CPU、記憶體隔離,也沒有網路隔離,但以服務隔離的角度,網路由防火牆防護,似乎不錯,於是實做了下,寫一篇分享。

環境:

相同作法應該適用在Ubuntu/Debian系列的實體機和VM內,不適用Container內(LXC、Docker)。
實驗環境是Debian 10 (Buster)。

安裝:

1. 需要安裝2個套件,包括debootstrap和schroot。
# apt-get install debootstrap schroot

2. 建立Chroot系統目錄
# mkdir /home/chroot

(這裡有個選項,如果有額外的磁碟或分割區,目錄可以用ZFS,好處是ZFS有snapshot功能,能讓Chroot系統支援snapshot)

3. 產生Chroot系統的Template
# debootstrap buster /home/chroot/rootfs http://ftp.tw.debian.org/debian/
(debootstrap能產生Debian/Ubuntu系統的Rootfs,裡面會包含Standard版本的完整系統,不包含fstab之類的設定)

4. 把Template打包
# cd /home/chroot
# tar cjf chroot.tar.bz2 rootfs

設置Chroot系統:

1. 解壓縮並更名Template
# cd /home/chroot
# tar jxf chroot.tar.bz2
# mv rootfs webrootfs (預計要使用的Chroot系統名稱,這裡用webrootfs)

2. 編輯schroot的Chroot系統設定檔
# vi /etc/schroot/chroot.d/webrootfs.conf
[webrootfs]
description=Web Rootfs
directory=/home/chroot/webrootfs
root-users=root
users=myuser
type=directory

進入Chroot系統:

執行指令切換到Chroot系統
# schroot -c webrootfs

切換到Chroot系統後,所有的操作都在Chroot系統內,這時候可以安裝套件,準備執行環境。

操作Chroot系統與執行程式:

1. 啟動Chroot系統Session
# schroot -c webrootfs -b
執行後會出現一串像是:
webrootfs-b7738702-b79a-4376-9952-377570356804

這是這個Session的SessionID
(-c 指定Chroot系統名稱)
(-b 啟動Chroot系統,產生Session)

2. 查詢所有SessionID
# ls /run/schroot/mount/
所有SessionID都會紀錄在 /run/schroot/mount/

3. 進入Session
# schroot -r -c webrootfs-b7738702-b79a-4376-9952-377570356804
當SHELL出現(webrootfs)root 就是進入Session了
(-c 指定SessionID)
(-r 進入存在的Session)

4. 離開Session
# exit

5. 刪除Session
# schroot -e -c webrootfs-b7738702-b79a-4376-9952-377570356804
(-c 指定SessionID)
(-e 刪除存在的Session)

參考:

2021年2月18日

Python Qt (PySide) 起手勢

在Python討論區看到有人用PySide實作視窗程式,因此嘗試用PySide寫出Python版本的Qt樣板程式。

用過QtCreator的會發現,產生QWidget (或QDialog) 的Project後,會產生一個預設樣式的程式碼框架,但在PySide上,比較少看到,尤其加上Qt Designer之後,pyside-uic會產生UI Python程式碼,如何和其他程式碼整合,是個入門的門檻,因此寫一個PySide版的Template和作法。

PySide Template程式碼分3塊:
1. Main主程式
2. Dialog主體
3. Qt Designer產生的Dialog內容

Main主程式:
import sys
from PySide.QtGui import *
from PySide.QtCore import *

from main_widget import MainWidget

if __name__ == '__main__':
app = QApplication(sys.argv)
main_widget = MainWidget()
main_widget.show()

sys.exit(app.exec_())


Dialog主體:
from PySide.QtGui import *
from PySide.QtCore import *

from ui_text_dialog import Ui_Dialog

class MainWidget(QDialog):

ui = Ui_Dialog()

def __init__(self):
QDialog.__init__(self)
self.ui.setupUi(self)
self.ui.CloseButton.setText("關閉視窗")

self.ui.CloseButton.clicked.connect(self.on_CloseButton_clicked)

def on_CloseButton_clicked(self):
QApplication.exit(0)

Qt Designer產生的Dialog內容:
Qt設計師產生後,儲存的檔名以ui_開頭做為區別。

框架原理如下:
Main主程式內等同於Qt的main.cpp,主要放的是QApplication和包含Config在內其他初始化程式。

Dialog程式內放的是實際視窗的程式,等同於Qt的widget.cpp(dialog.cpp)和widget.h(dialog.h),實際的視窗程式所在,包括視窗功能,Signal/Slot定義、連接、動作都寫在這裡。
Dialog程式內的關鍵有3個:
1. 繼承QDialog
    class MainWidget(QDialog)

2. 在 __init__(self) 建構子內,要呼叫QDialog的建構子
    QDialog.__init__(self)

3. 透過 setupUi() 把 Qt Designer 的 ui_dialog 整個載入,並把dialog指定到 Qt Designer 的 ui_dialog
self.ui.setupUi(self)

Qt Designer產生的ui_dialog,直接由Qt Designer產生就能套用,objectName要設定,Signal/Slot不寫在這,直接寫在Dialog的實體中。

程式碼: