nanoFramework 筆記 - ESP32 連接 I2C 裝置
0 |
昨天介紹完可以用 C# 寫 ESP32 開發板程式的美妙開源平台 - nanoFramework,我迫不及待想把先前 Arduino C++ 寫的作品移植過來,但光是想接 I2C OLED 顯示器輸出文字就卡住三、四個小時...
其實早有心理準備,nanoFramework 仍在發展初期,用的 NuGet 套件全是 Preview 版,一方面參考文件、討論少,常缺乏現成可用的程式庫;另一方面找到的教學或範例也常因 API 規格修改失效,這是當先鋒部隊一定會面對的挑戰。跟寫 Arduino C++ 相比,過去那種「遇到任何問題,隨手一查就有滿滿的教學範例跟程式庫任你挑」的尊榮體驗不復存在,像是貴公子逃家,開始粗茶淡飯過日子。但是! 能用 C# 寫程式就是爽,付出一些代價也是值得的。
網路上 nanoFramework 整合 I2C 範例不多,用 ESP32 的更少。找到網友三年前寫的 SSD1306 OLED nanoFramework NuGet 程式庫,但因核心版本差異過大無法執行,加上原作者只測過 128x32 解析度的版本,不確定跟手上在用的 128x64 是否有差異。於是改了抓原始碼回來改,參考已在 Arduino C++ 驗證 OK 的 Adafruit 程式庫寫法進行調整,但怎麼改 OLED 顯示器都毫無動靜。搞了很久,直到我回頭寫了小程式去掃 I2C 位址,才發現問題出在 ESP32 偵測不到 I2C 裝置。朝此方向深入研究,這才知道,ESP32 因開發板種類眾多,Pin 腳設定依版本不同,故要參照 nanoFramework.Hardware.Esp32 套件使用 Configuration.SetPinFunction(..., DeviceFunction.I2C_DATA) 重新定義 I2C 腳位(我的板子,I2C1_DATA 是 21、I2C1_CLOCK 是 22),這才測試成功,感動~
整理這次的心得:
- nanoFramework 處理 I2C 有兩套 NuGet 程式庫,nanoFramework.Windows.Devices.I2c 跟 nanoFramework.System.Device.I2c,Windows.Devices.I2c 下載次數多,更新日期新,網路範例幾乎也都是用 Windows.Devices.I2c,但它已被標註為過時(This package has been deprecated),這類 UWP API 將由 .NET IoT API 取代,使用時別西瓜偎大邊,建議改用 System.Device.I2c。
- 寫 nanoFramework 不像 Arduino C++ 有一堆現成範例、程式庫裝了就可以跑。故測試時請從根本開始做起,例如要接 I2C 設備,建議從偵測 I2C 位址是否存在開始,不要像我誤以為是程式庫與裝罝不相容,查了大半天才發現是漏設 Pin 腳沒設,白花時間。
附上掃瞄 I2C 位址的程式範例:
實測如下:(雖然是很簡單的範例,我還是預先切好類別程式庫專案方便未來共用。這才是寫專案的正確姿勢,換回 C# 我終於擺脫老鳥魔咒了)using nanoFramework.Hardware.Esp32; using System; using System.Collections; using System.Device.I2c; namespace Guineapig.nfEsp32Lib.I2C { /// <summary> /// I2C device address scanner /// </summary> public class Scanner { /// <summary> /// Scan I2C device addresses /// </summary> /// <param name="busId">I2C bus id, 1 or 2. default 1</param> /// <param name="clockPin">Clock pin</param> /// <param name="dataPin">Data pin</param> /// <returns></returns> public static byte[] Scan(int busId = 1, int clockPin = 22, int dataPin = 21) { switch (busId) { case 1: Configuration.SetPinFunction(clockPin, DeviceFunction.I2C1_CLOCK); Configuration.SetPinFunction(dataPin, DeviceFunction.I2C1_DATA); break; case 2: Configuration.SetPinFunction(clockPin, DeviceFunction.I2C2_CLOCK); Configuration.SetPinFunction(dataPin, DeviceFunction.I2C2_DATA); break; default: throw new ArgumentException($"Unsupported busId - {busId}"); } ArrayList list = new(); SpanByte b = new SpanByte(new byte[1]); // I2C address range from https://learn.adafruit.com/i2c-addresses/the-list for (byte addr = 0x0E; addr <= 0x77; addr++) { using (var dev = I2cDevice.Create(new I2cConnectionSettings(busId, addr))) { var r = dev.Read(b); if (r.Status == I2cTransferStatus.SlaveAddressNotAcknowledged) continue; if (r.Status == I2cTransferStatus.FullTransfer || r.Status == I2cTransferStatus.PartialTransfer) { list.Add(addr); } } } return list.ToArray(typeof(byte)) as byte[]; } } }
- 關於常見的 I2C 位址,Adafruit 有份完整清單可以參考,不愧是 Arduino 界的善心員外。
- 受限於嵌入式裝置的運算能力及記憶體、儲存空間,加上尚在發展初期,nanoFramework 與完整 .NET 功能有很大的差距,沒有 LINQ 在預期之內,連泛型(如 List<T>)也仍在開發中,未來才會加入,現階段要先用古老的 ArrayList 頂著,甚至也沒有 StringBuilder,有重回 .NET 1.0 的感覺。但老話一句,能用 C# 就是開心。
When using ESP32 + nanoFramework to connect I2C device, don't forget to Configuration.SetPinFunction() to set I2C pins.
Comments
Be the first to post a comment