Showing posts with label #ArduinoOLED. Show all posts
Showing posts with label #ArduinoOLED. Show all posts

2026/08/31

多年前寫的Android App又被 Google 要求升級了!

 幾乎每隔幾年, Google Play store 都會要求更新 Android SDK 的版本. 今年要求更新到 API Level 36.



這款 Color LED Controller App 是我和我兒子多年前開發的. 主要功能是透過藍牙連線來控制 RGB LED. 目前在 Google Play 上獲得 5.0 星評分,下載次數超過 10 萬次。

用途:搭配 Arduino 或相容模組,讓手機能直接控制 LED 顏色與亮度。



特色:

支援多種 Android 裝置

簡單直覺的操作介面

適合 Maker、學生或喜歡 DIY 燈光效果的使用者

版本更新:目前已更新至 v3.4,符合 Google 最新 Android API Level 要求。

如果你有興趣的話, 我的部落格裡有完整的免費的軟硬體教學+程式碼完全公開~ 😊


硬體教學:

https://stonez56.blogspot.com/2014/09/arduino-android-led.html

手機 App 教學:

https://stonez56.blogspot.com/2015/02/arduino-project-android-control-color.html


2026/05/21

ESP32-C3 與 MPU6050 的進階開發

 


引言 (Introduction)

在嵌入式開發與 Maker 的世界裡,微機電感測器(MEMS)的讀取通常是踏入硬體互動的第一步。然而,當我們試圖在底層硬體(ESP32-C3 SuperMini)上,將生硬的 MPU6050 角度數據轉化為流暢、具備擬真物理互動的 OLED 遊戲或模擬時,往往會踩入一連串教科書上沒寫的「硬體與演算法地雷」。

本篇文章將完整記錄如何從開機死鎖(Hang)的黑畫面一步步排查,導入精確的感測器融合(Sensor Fusion)與全零點校正,並最終在記憶體受限的單晶片上,徒手刻出一個支援 30 顆方塊實時碰撞、相互依靠堆疊的輕量級物理引擎。

第一章:幽靈的開機死鎖——解密 ESP32-C3 的 Strapping Pins 陷阱

很多初學者在將 MPU6050 連接上 ESP32-C3 SuperMini 並使用預設的 I2C 腳位(SDA=8, SCL=9)後,會遇到一個極其詭異的現象:程式燒錄成功(Upload OK),但序列埠監視器(Serial Monitor)永遠只印出第一行引導碼:

Plaintext
ESP-ROM:esp32c3-api1-20210207

隨後整個系統陷入無限期的死鎖,連 setup() 裡最頂端的 Serial.println() 都來不及執行。

核心原因分析:

問題不在軟體,而在硬體的「開機配置腳(Strapping Pins)」。ESP32-C3 晶片在硬體重置(Reset)的瞬間,會去偵測 GPIO 8 與 GPIO 9 的電位狀態,以決定晶片是要進入「SPI Boot 正常開機」還是「ROM Boot 燒錄模式」。

當 MPU6050 模組上的 4.7kΩ I2C 上拉電阻(Pull-up Resistors)直接把 GPIO 8 的電位拉高或拉低到異常狀態時,晶片在開機瞬間就會誤判啟動模式,進而硬體級鎖死。

工程解決方案:

徹底避開衝突腳位,在軟體中手動重新映射通訊軌道。我們將 I2C 匯流排改至安全且完全不影響開機配置的 GPIO 4 (SDA)GPIO 5 (SCL)

C++
Wire.begin(4, 5); // 徹底避開 GPIO 8 開機地雷
Wire.setTimeOut(50); // 設置硬體超時防線

透過更換安全引腳與加入 setTimeOut 超時防線,即便硬體通訊中斷,底層函式庫也不會再讓 CPU 無限期死等硬體回應。

第二章:桌上的「鬧鬼」滑行——全感測器零點深度校正

當 I2C 通訊順利暢通後,我們嘗試在 OLED 上繪製一個基準框,並讓小球隨角度滑動。此時第二個經典盲點浮現:將板子平放在絕對靜止的桌面上,小球卻在幾秒鐘內自己緩緩「滑出平台」撞牆。

為什麼單純校正陀螺儀(Gyro)是不夠的?

一般的範例程式只會在開機時對陀螺儀進行取樣取平均,以扣除角速度的漂移(Drift)。然而,放在桌上靜止時,加速度計(Accelerometer)本身也帶有硬體偏置误差(Offset)

受限於晶片封裝、洞洞板表面的銲接傾角、或是桌面本身的微小傾斜,加速度計讀到的 $X, Y$ 軸絕對不會是完美的 0.0。由於互補濾波器(Sensor Fusion)會不斷參考加速度計的絕對角度,這 $\pm 2^\circ \sim 3^\circ$ 的微小硬體誤差會被誤判為持續的傾斜,導致小球產生「自動滑行」的靈異現象。

深度水平校正演算法:

我們必須在開機時執行取樣(例如 200 次),不僅記錄陀螺儀偏置,更要把當前桌面狀態下的加速度計絕對三角角度(Roll/Pitch)直接記錄下來,並在 loop 運算中扎實地扣除它:

C++
// 減去開機時的桌面偏置,將當前狀態定義為「絕對 0 度基準」
float accRoll  = rawAccRoll - errorAccRoll;
float accPitch = rawAccPitch - errorAccPitch;

這樣一來,不論銲接再怎麼歪、桌面再怎麼斜,只要放在桌上不動,數值就會死死咬在 0 度,水晶球便能滑順地定格在正中央。

第三章:從冰面滑行到重力暴衝——非線性物理與 LERP 緩動

為了提升視覺的擬真度與互動的爽快感,系統的滾動速度不能是死板的線性移動,必須引入真正的加速度(Acceleration)與慣性阻尼(Friction)

  1. 重力加速度累積:傾斜角度(Roll/Pitch)作為力(Force)施加在物體上,速度隨時間持續累積($V = V + a$)。手腕傾斜角度越大,施加的加速度越高,物體會呈現指數級的暴衝感。

  2. LERP (線性插值) 濾波緩動:為了消除未過濾的手震雜訊,我們不讓方塊直接瞬移到目標物理座標,而是利用緩動公式:

    $$\text{Current Position} += (\text{Target Position} - \text{Current Position}) \times \text{Factor}$$

    這讓物體在畫面上移動時,產生了一種如同在冰面上滑行的油潤液壓感與重力慣性。

第四章:極限壓榨效能——30 顆粒子實時碰撞堆疊引擎

專案的終極挑戰,是在單晶片上實施群體粒子模擬:在 OLED 畫面上繪製最大邊界框,生成 30 個 $4 \times 4$ 像素的獨立小方塊,當它們受到重力滑向特定牆角時,必須互相堆疊依靠、自然緊密排列,且彼此絕不產生重疊或穿透。

幽靈隱形牆的 Debug 經驗:

在初期測試中,主迴圈明明只命令 U8g2 繪製 30 個方塊,但當方塊滑落到底部時,卻會在距離底壁還有 10 像素的地方「憑空卡住」,死死地排成一列,彷彿撞上了隱形牆。

經查,元凶在於交叉碰撞檢查(Axis-Aligned Bounding Box, AABB)的內部迴圈上限被硬編碼寫死了常數:

C++
for (int j = 0; j < 30; j++) { ... } // ❌ 越界讀取地雷

當這個常數與實際陣列大小產生偏差時,C++ 在執行碰撞判定時,會讀取到陣列邊界之外的未初始化記憶體垃圾數據(Uninitialized Memory)。這些垃圾數據在物理邏輯上被誤判為「已經死死躺在底部的方塊」,導致真實的 30 個方塊在移動時,提前撞上了這些看不到的記憶體幽靈。

工業級物理引擎解法:

  1. 內存徹底清空:開機時使用 memset(boxes, 0, sizeof(boxes)); 徹底洗淨陣列,抹除一切硬體隨機殘留值。

  2. 多重鬆弛迭代(Relaxation Iterations):當多個方塊在牆角同時擠壓時,方塊 A 撞 B、B 又同時撞牆。我們在單次 loop 內加入 3 次迭代解算(Sweep),在極短的時間差 $dt$ 內反覆修正重疊座標,讓 30 顆方塊能夠展現出如同沙子般完美、扎實的依靠堆疊視覺特效。

C++
// 核心多重鬆弛迭代碰撞檢查結構
for (int k = 0; k < 3; k++) { 
  for (int i = 0; i < NUM_BOXES; i++) {
    // 預計移動的新座標計算與 AABB 矩形碰撞判定...
    // 發生重疊時,精確倒推邊緣座標並將該軸向速度(vx/vy)安全歸零
  }
}

結語 (Conclusion)

透過這一連串的硬體引腳更換、雙重零點校正、LERP 緩動機制,以及修正越界內存的碰撞演算法,原本生硬跳動的感測器訊號,在 ESP32-C3 上成功被賦予了真實的動態生命力。

完整的粒子堆疊物理引擎代碼已開源(詳見上文),不論你是想為你的機器人專案加入更滑順的角度追蹤,還是想在小螢幕上實作精緻的街機互動,這套整合了 Sytem Architecture (SA) 穩定防禦UI/UX 視覺美學 的底層框架,都能為你的下一個 Maker 作品打下最扎實的技術基石!