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的實體中。

程式碼: