From 14449a46d5dc95b719ccb485df22c4e7de516b78 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 10 Jul 2026 14:39:51 +0200 Subject: [PATCH 1/9] Adding M5Stack StickS3 board package --- README.md | 8 + .../M5StickS3TestApp/M5StickS3TestApp.nfproj | 110 ++++++++ Tests/M5StickS3TestApp/Program.cs | 259 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 19 ++ Tests/M5StickS3TestApp/packages.config | 26 ++ azure-pipelines.yml | 6 +- nanoFramework.M5Stack.sln | 18 ++ nanoFramework.M5StickS3.nuspec | 41 +++ nanoFramework.M5StickS3/M5StickS3.cs | 218 +++++++++++++++ .../Properties/AssemblyInfo.cs | 11 + nanoFramework.M5StickS3/Screen.cs | 120 ++++++++ nanoFramework.M5StickS3/StickS3St7789.cs | 80 ++++++ .../nanoFramework.M5StickS3.nfproj | 123 +++++++++ nanoFramework.M5StickS3/packages.config | 27 ++ nanoFramework.M5StickS3/packages.lock.json | 151 ++++++++++ 15 files changed, 1216 insertions(+), 1 deletion(-) create mode 100644 Tests/M5StickS3TestApp/M5StickS3TestApp.nfproj create mode 100644 Tests/M5StickS3TestApp/Program.cs create mode 100644 Tests/M5StickS3TestApp/Properties/AssemblyInfo.cs create mode 100644 Tests/M5StickS3TestApp/packages.config create mode 100644 nanoFramework.M5StickS3.nuspec create mode 100644 nanoFramework.M5StickS3/M5StickS3.cs create mode 100644 nanoFramework.M5StickS3/Properties/AssemblyInfo.cs create mode 100644 nanoFramework.M5StickS3/Screen.cs create mode 100644 nanoFramework.M5StickS3/StickS3St7789.cs create mode 100644 nanoFramework.M5StickS3/nanoFramework.M5StickS3.nfproj create mode 100644 nanoFramework.M5StickS3/packages.config create mode 100644 nanoFramework.M5StickS3/packages.lock.json diff --git a/README.md b/README.md index fc64aff6..33f85bae 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ | nanoFramework.M5Core | [![Build Status](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_apis/build/status/nanoFramework.M5Stack?repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main)](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_build/latest?definitionId=52&repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main) | [![NuGet](https://img.shields.io/nuget/v/nanoFramework.M5Core.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.M5Stack/) | | nanoFramework.M5Stick | [![Build Status](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_apis/build/status/nanoFramework.M5Stack?repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main)](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_build/latest?definitionId=52&repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main) | [![NuGet](https://img.shields.io/nuget/v/nanoFramework.M5StickC.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.M5StickC/) | | nanoFramework.M5StickCPlus | [![Build Status](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_apis/build/status/nanoFramework.M5Stack?repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main)](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_build/latest?definitionId=52&repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main) | [![NuGet](https://img.shields.io/nuget/v/nanoFramework.M5StickCPlus.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.M5StickCPlus/) | +| nanoFramework.M5StickS3 | [![Build Status](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_apis/build/status/nanoFramework.M5Stack?repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main)](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_build/latest?definitionId=52&repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main) | [![NuGet](https://img.shields.io/nuget/v/nanoFramework.M5StickS3.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.M5StickS3/) | | nanoFramework.M5Core2 | [![Build Status](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_apis/build/status/nanoFramework.M5Stack?repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main)](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_build/latest?definitionId=52&repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main) | [![NuGet](https://img.shields.io/nuget/v/nanoFramework.M5Core2.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.M5Core2/) | | nanoFramework.M5CoreS3 | [![Build Status](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_apis/build/status/nanoFramework.M5Stack?repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main)](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_build/latest?definitionId=52&repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main) | [![NuGet](https://img.shields.io/nuget/v/nanoFramework.M5CoreS3.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.M5CoreS3/) | | nanoFramework.Fire | [![Build Status](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_apis/build/status/nanoFramework.M5Stack?repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main)](https://dev.azure.com/nanoframework/nanoFramework.M5Stack/_build/latest?definitionId=52&repoName=nanoframework%2FnanoFramework.M5Stack&branchName=main) | [![NuGet](https://img.shields.io/nuget/v/nanoFramework.Fire.svg?label=NuGet&style=flat&logo=nuget)](https://www.nuget.org/packages/nanoFramework.Fire/) | @@ -28,6 +29,7 @@ These NuGet packages provide a support for M5Stack products: - [Core (gray) and Basic](https://docs.m5stack.com/en/products?id=core) - [M5StickC](https://docs.m5stack.com/en/core/m5stickc) - [M5StickCPlus](https://docs.m5stack.com/en/core/m5stickc_plus) +- [M5StickS3](https://docs.m5stack.com/en/core/M5StickS3) - [M5Core2](https://docs.m5stack.com/en/core/core2) - [CoreS3](https://docs.m5stack.com/en/core/CoreS3) - [Fire](https://docs.m5stack.com/en/core/fire) @@ -60,6 +62,12 @@ For the M5StickCPlus: nanoff --target M5StickCPlus --update --serialport COM3 ``` +For the M5StickS3: + +```shell +nanoff --target ESP32_S3_OCTAL --update --serialport COM3 +``` + For the M5Core2, Tough and Fire: ```shell diff --git a/Tests/M5StickS3TestApp/M5StickS3TestApp.nfproj b/Tests/M5StickS3TestApp/M5StickS3TestApp.nfproj new file mode 100644 index 00000000..ac076324 --- /dev/null +++ b/Tests/M5StickS3TestApp/M5StickS3TestApp.nfproj @@ -0,0 +1,110 @@ + + + + $(MSBuildExtensionsPath)\nanoFramework\v1.0\ + + + + Debug + AnyCPU + {11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC12F12-B6DA-414F-8E30-DF57F5334B2D} + Exe + Properties + 512 + M5StickS3TestApp + M5StickS3TestApp + v1.0 + + + + + + + + + + + + ..\..\packages\nanoFramework.Iot.Device.Bmm150.1.2.991\lib\Iot.Device.Bmm150.dll + + + ..\..\packages\nanoFramework.Iot.Device.Button.1.2.1002\lib\Iot.Device.Button.dll + + + ..\..\packages\nanoFramework.Iot.Device.Common.NumberHelper.1.2.862\lib\Iot.Device.Common.NumberHelper.dll + + + ..\..\packages\nanoFramework.Iot.Device.Bmi270.1.0.9\lib\Iot.Device.Bmi270.dll + + + ..\..\packages\nanoFramework.Iot.Device.M5Pm1.1.0.1\lib\Iot.Device.M5Pm1.dll + + + ..\..\packages\nanoFramework.CoreLibrary.1.17.12\lib\mscorlib.dll + + + ..\..\packages\nanoFramework.Graphics.1.2.53\lib\nanoFramework.Graphics.dll + + + ..\..\packages\nanoFramework.Graphics.1.2.53\lib\nanoFramework.Graphics.Core.dll + + + ..\..\packages\nanoFramework.Hardware.Esp32.1.6.40\lib\nanoFramework.Hardware.Esp32.dll + + + ..\..\packages\nanoFramework.ResourceManager.1.2.32\lib\nanoFramework.ResourceManager.dll + + + ..\..\packages\nanoFramework.Runtime.Events.1.11.37\lib\nanoFramework.Runtime.Events.dll + + + ..\..\packages\nanoFramework.Runtime.Native.1.7.11\lib\nanoFramework.Runtime.Native.dll + + + ..\..\packages\nanoFramework.System.Collections.1.5.75\lib\nanoFramework.System.Collections.dll + + + ..\..\packages\nanoFramework.UnitsNet.ElectricPotential.5.76.15\lib\netnano1.0\nanoFramework.UnitsNet.ElectricPotential.dll + + + ..\..\packages\nanoFramework.UnitsNet.Temperature.5.76.15\lib\netnano1.0\nanoFramework.UnitsNet.Temperature.dll + + + ..\..\packages\nanoFramework.System.Buffers.Binary.BinaryPrimitives.1.2.862\lib\System.Buffers.Binary.BinaryPrimitives.dll + + + ..\..\packages\nanoFramework.System.Device.Gpio.1.1.62\lib\System.Device.Gpio.dll + + + ..\..\packages\nanoFramework.System.Device.I2c.1.1.29\lib\System.Device.I2c.dll + + + ..\..\packages\nanoFramework.System.Device.Model.1.2.862\lib\System.Device.Model.dll + + + ..\..\packages\nanoFramework.System.Diagnostics.Stopwatch.1.2.862\lib\System.Diagnostics.Stopwatch.dll + + + ..\..\packages\nanoFramework.System.Math.1.5.116\lib\System.Math.dll + + + ..\..\packages\nanoFramework.System.Numerics.1.2.862\lib\System.Numerics.dll + + + ..\..\packages\nanoFramework.System.Threading.1.1.52\lib\System.Threading.dll + + + ..\..\packages\UnitsNet.nanoFramework.Temperature.5.75.0\lib\UnitsNet.Temperature.dll + + + + + + + + + + + + \ No newline at end of file diff --git a/Tests/M5StickS3TestApp/Program.cs b/Tests/M5StickS3TestApp/Program.cs new file mode 100644 index 00000000..f98f4387 --- /dev/null +++ b/Tests/M5StickS3TestApp/Program.cs @@ -0,0 +1,259 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using nanoFramework.M5Stack; +using System.Diagnostics; +using System.Drawing; +using System.Threading; +using UnitsNet; +using Iot.Device.M5Pm1; + +Debug.WriteLine("Hello M5StickS3 from nanoFramework!"); + +int pmicDeviceId = M5StickS3.Power.GetDeviceId(); +Debug.WriteLine($"M5PM1 device ID: 0x{pmicDeviceId:X4} (expected 0x{M5Pm1.DeviceId:X4})."); +Debug.WriteLine("M5PM1 initialized. Reporting power telemetry..."); + +M5StickS3.InitializeScreen(); + +ushort[] buffer = new ushort[100]; +FillBuffer(buffer, Color.Red.ToBgr565()); +Screen.Write(0, 0, 10, 10, buffer); + +FillBuffer(buffer, Color.Green.ToBgr565()); +Screen.Write((ushort)(135 - 10), 0, 10, 10, buffer); + +FillBuffer(buffer, Color.Blue.ToBgr565()); +Screen.Write(0, (ushort)(240 - 10), 10, 10, buffer); + +FillBuffer(buffer, Color.White.ToBgr565()); +Screen.Write((ushort)(135 - 10), (ushort)(240 - 10), 10, 10, buffer); + +bool screenEnabled = true; +M5StickS3.ButtonA.Press += (s, e) => +{ + screenEnabled = !screenEnabled; + Screen.Enabled = screenEnabled; + Debug.WriteLine($"Button A pressed, screen enabled: {screenEnabled}"); +}; + +M5StickS3.ButtonB.Press += (s, e) => +{ + M5StickS3.External5VEnabled = !M5StickS3.External5VEnabled; + Debug.WriteLine($"Button B pressed, ext 5V enabled: {M5StickS3.External5VEnabled}"); +}; + +while (true) +{ + var accel = M5StickS3.AccelerometerGyroscope.GetAccelerometer(); + var gyro = M5StickS3.AccelerometerGyroscope.GetGyroscope(); + bool isCharging = M5StickS3.IsCharging; + ElectricPotential batteryVoltage = M5StickS3.BatteryVoltage; + ElectricPotential vbusVoltage = M5StickS3.VBusVoltage; + ElectricPotential outputVoltage = M5StickS3.Power.GetOutputVoltage(); + PowerSource powerSource = M5StickS3.Power.GetPowerSource(); + bool gpio0 = (bool)M5StickS3.Power.ReadGpio(Pin.Gpio0); + int batteryMv = ToMillivolts(batteryVoltage); + int vbusMv = ToMillivolts(vbusVoltage); + int out5vMv = ToMillivolts(outputVoltage); + int batteryPercent = EstimateBatteryPercent(batteryMv, isCharging, vbusMv); + string batteryStatusText = batteryPercent >= 0 + ? $"BAT {batteryPercent,3}% CHG{(isCharging ? "ON" : "OFF")}" + : "BAT CHARGING"; + string batteryVoltageText = $"BATV {batteryMv,4}MV"; + + Debug.WriteLine($"Battery : {batteryMv} mV"); + Debug.WriteLine($"VBUS : {vbusMv} mV"); + Debug.WriteLine($"5V out : {out5vMv} mV"); + Debug.WriteLine($"Source : {(int)powerSource}"); + Debug.WriteLine($"Charging: {isCharging}"); + Debug.WriteLine($"GPIO0 : {gpio0}"); + Debug.WriteLine($"Ext5V : {M5StickS3.External5VEnabled}"); + Debug.WriteLine($"Accel=({accel.X:N2},{accel.Y:N2},{accel.Z:N2})g"); + Debug.WriteLine($"Gyro =({gyro.X:N2},{gyro.Y:N2},{gyro.Z:N2}) dps"); + + const int uiScale = 1; + DrawTelemetryLine(12, FitTelemetryText(batteryStatusText), Color.Yellow, Color.Black, uiScale); + DrawTelemetryLine(22, FitTelemetryText(batteryVoltageText), Color.Yellow, Color.Black, uiScale); + DrawTelemetryLine(32, FitTelemetryText($"AX {FormatSigned(accel.X)}"), Color.Lime, Color.Black, uiScale); + DrawTelemetryLine(42, FitTelemetryText($"AY {FormatSigned(accel.Y)}"), Color.Lime, Color.Black, uiScale); + DrawTelemetryLine(52, FitTelemetryText($"AZ {FormatSigned(accel.Z)}"), Color.Lime, Color.Black, uiScale); + DrawTelemetryLine(62, FitTelemetryText($"GX {FormatSigned(gyro.X)}"), Color.Cyan, Color.Black, uiScale); + DrawTelemetryLine(72, FitTelemetryText($"GY {FormatSigned(gyro.Y)}"), Color.Cyan, Color.Black, uiScale); + DrawTelemetryLine(82, FitTelemetryText($"GZ {FormatSigned(gyro.Z)}"), Color.Cyan, Color.Black, uiScale); + + Thread.Sleep(1000); +} + +static void FillBuffer(ushort[] buffer, ushort color) +{ + for (int i = 0; i < buffer.Length; i++) + { + buffer[i] = color; + } +} + +static int EstimateBatteryPercent(int millivolts, bool isCharging, int vbusMillivolts) +{ + const int emptyMv = 3000; + const int fullMv = 4200; + + // When USB power is present and charging is active, some PMIC states can report + // very low battery voltage transiently. Report "charging" instead of a fake 0%. + if (isCharging && vbusMillivolts > 4300 && millivolts < emptyMv) + { + return -1; + } + + if (millivolts <= emptyMv) + { + return isCharging ? 1 : 0; + } + + if (millivolts >= fullMv) + { + return 100; + } + + return (millivolts - emptyMv) * 100 / (fullMv - emptyMv); +} + +static int ToMillivolts(ElectricPotential voltage) +{ + int millivolts = (int)voltage.Millivolts; + if (millivolts > 0) + { + return millivolts; + } + + return (int)(voltage.Volts * 1000.0); +} + +static string FormatSigned(double value) +{ + int scaled = (int)(value * 100); + int absScaled = scaled < 0 ? -scaled : scaled; + int integer = absScaled / 100; + int fraction = absScaled % 100; + string sign = scaled >= 0 ? "+" : "-"; + return $"{sign}{integer}.{fraction:D2}"; +} + +static string FitTelemetryText(string text) +{ + const int maxChars = 14; + + if (text.Length <= maxChars) + { + return text; + } + + return text.Substring(0, maxChars); +} + +static void DrawTelemetryLine(ushort y, string text, Color foreground, Color background, int scale) +{ + const int width = 135; + int height = (7 * scale) + 1; + int charWidth = (5 * scale) + 1; + + ushort fg = foreground.ToBgr565(); + ushort bg = background.ToBgr565(); + ushort[] lineBuffer = new ushort[width * height]; + + for (int i = 0; i < lineBuffer.Length; i++) + { + lineBuffer[i] = bg; + } + + string upper = text.ToUpper(); + int maxChars = width / charWidth; + if (upper.Length > maxChars) + { + upper = upper.Substring(0, maxChars); + } + + for (int c = 0; c < upper.Length; c++) + { + byte[] glyph = GetGlyph(upper[c]); + int xBase = c * charWidth; + for (int row = 0; row < 7; row++) + { + byte bits = glyph[row]; + for (int col = 0; col < 5; col++) + { + if ((bits & (1 << (4 - col))) != 0) + { + int x = xBase + (col * scale); + int yBase = row * scale; + for (int sy = 0; sy < scale; sy++) + { + for (int sx = 0; sx < scale; sx++) + { + int px = x + sx; + int py = yBase + sy; + int index = py * width + px; + if (index >= 0 && index < lineBuffer.Length) + { + lineBuffer[index] = fg; + } + } + } + } + } + } + } + + Screen.Write(0, y, width, (ushort)height, lineBuffer); +} + +static byte[] GetGlyph(char c) +{ + switch (c) + { + case 'A': return new byte[] { 0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11 }; + case 'B': return new byte[] { 0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E }; + case 'C': return new byte[] { 0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E }; + case 'D': return new byte[] { 0x1E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E }; + case 'E': return new byte[] { 0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F }; + case 'F': return new byte[] { 0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10 }; + case 'G': return new byte[] { 0x0E, 0x11, 0x10, 0x13, 0x11, 0x11, 0x0E }; + case 'H': return new byte[] { 0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11 }; + case 'I': return new byte[] { 0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E }; + case 'J': return new byte[] { 0x01, 0x01, 0x01, 0x01, 0x11, 0x11, 0x0E }; + case 'K': return new byte[] { 0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11 }; + case 'L': return new byte[] { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F }; + case 'M': return new byte[] { 0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11 }; + case 'N': return new byte[] { 0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x11 }; + case 'O': return new byte[] { 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E }; + case 'P': return new byte[] { 0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10 }; + case 'Q': return new byte[] { 0x0E, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0D }; + case 'R': return new byte[] { 0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11 }; + case 'S': return new byte[] { 0x0F, 0x10, 0x10, 0x0E, 0x01, 0x01, 0x1E }; + case 'T': return new byte[] { 0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; + case 'U': return new byte[] { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E }; + case 'V': return new byte[] { 0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04 }; + case 'W': return new byte[] { 0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A }; + case 'X': return new byte[] { 0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11 }; + case 'Y': return new byte[] { 0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04 }; + case 'Z': return new byte[] { 0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F }; + case '0': return new byte[] { 0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E }; + case '1': return new byte[] { 0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E }; + case '2': return new byte[] { 0x0E, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1F }; + case '3': return new byte[] { 0x1E, 0x01, 0x01, 0x0E, 0x01, 0x01, 0x1E }; + case '4': return new byte[] { 0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02 }; + case '5': return new byte[] { 0x1F, 0x10, 0x10, 0x1E, 0x01, 0x01, 0x1E }; + case '6': return new byte[] { 0x0E, 0x10, 0x10, 0x1E, 0x11, 0x11, 0x0E }; + case '7': return new byte[] { 0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08 }; + case '8': return new byte[] { 0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E }; + case '9': return new byte[] { 0x0E, 0x11, 0x11, 0x0F, 0x01, 0x01, 0x0E }; + case ':': return new byte[] { 0x00, 0x04, 0x04, 0x00, 0x04, 0x04, 0x00 }; + case '.': return new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04 }; + case '+': return new byte[] { 0x00, 0x04, 0x04, 0x1F, 0x04, 0x04, 0x00 }; + case '-': return new byte[] { 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00 }; + case '/': return new byte[] { 0x01, 0x02, 0x02, 0x04, 0x08, 0x08, 0x10 }; + case '%': return new byte[] { 0x19, 0x19, 0x02, 0x04, 0x08, 0x13, 0x13 }; + case ' ': return new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + default: return new byte[] { 0x1F, 0x11, 0x01, 0x02, 0x04, 0x00, 0x04 }; + } +} diff --git a/Tests/M5StickS3TestApp/Properties/AssemblyInfo.cs b/Tests/M5StickS3TestApp/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a6dc39c4 --- /dev/null +++ b/Tests/M5StickS3TestApp/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("M5StickS3TestApp")] +[assembly: AssemblyDescription("M5StickS3 test application")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("nanoframework")] +[assembly: AssemblyProduct("M5StickS3TestApp")] +[assembly: AssemblyCopyright("Copyright (c) .NET Foundation and Contributors")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tests/M5StickS3TestApp/packages.config b/Tests/M5StickS3TestApp/packages.config new file mode 100644 index 00000000..92f64398 --- /dev/null +++ b/Tests/M5StickS3TestApp/packages.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7c1d621e..f3bb3ef7 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -78,6 +78,10 @@ steps: parameters: nugetPackageName: 'nanoFramework.M5CoreS3' +- template: azure-pipelines-templates/class-lib-package.yml@templates + parameters: + nugetPackageName: 'nanoFramework.M5StickS3' + - template: azure-pipelines-templates/class-lib-package.yml@templates parameters: nugetPackageName: 'nanoFramework.AtomLite' @@ -117,7 +121,7 @@ steps: tag: v$(MY_NUGET_VERSION) title: 'M5Stack Board Support Packages v$(MY_NUGET_VERSION)' releaseNotesSource: inline - releaseNotesInline: 'Check the [changelog]($(Build.Repository.Uri)/blob/$(Build.SourceBranchName)/CHANGELOG.md).

Install from NuGet


The following NuGet packages are available for download from this release:
:package: [M5Core](https://www.nuget.org/packages/nanoFramework.M5Core/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION).
:package: [M5Core2](https://www.nuget.org/packages/nanoFramework.M5Core2/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION).
:package: [M5CoreS3](https://www.nuget.org/packages/nanoFramework.M5CoreS3/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION).
:package: [M5StickC](https://www.nuget.org/packages/nanoFramework.M5StickC/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)
:package: [M5StickCPlus](https://www.nuget.org/packages/nanoFramework.M5StickCPlus/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)
:package: [M5Stack Fire](https://www.nuget.org/packages/nanoFramework.Fire/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)
:package: [Atom Lite](https://www.nuget.org/packages/nanoFramework.AtomLite/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)
:package: [Atom Matrix](https://www.nuget.org/packages/nanoFramework.AtomMatrix/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)
:package: [Tough](https://www.nuget.org/packages/nanoFramework.Tough/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)' + releaseNotesInline: 'Check the [changelog]($(Build.Repository.Uri)/blob/$(Build.SourceBranchName)/CHANGELOG.md).

Install from NuGet


The following NuGet packages are available for download from this release:
:package: [M5Core](https://www.nuget.org/packages/nanoFramework.M5Core/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION).
:package: [M5Core2](https://www.nuget.org/packages/nanoFramework.M5Core2/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION).
:package: [M5CoreS3](https://www.nuget.org/packages/nanoFramework.M5CoreS3/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION).
:package: [M5StickC](https://www.nuget.org/packages/nanoFramework.M5StickC/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)
:package: [M5StickCPlus](https://www.nuget.org/packages/nanoFramework.M5StickCPlus/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)
:package: [M5StickS3](https://www.nuget.org/packages/nanoFramework.M5StickS3/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)
:package: [M5Stack Fire](https://www.nuget.org/packages/nanoFramework.Fire/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)
:package: [Atom Lite](https://www.nuget.org/packages/nanoFramework.AtomLite/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)
:package: [Atom Matrix](https://www.nuget.org/packages/nanoFramework.AtomMatrix/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)
:package: [Tough](https://www.nuget.org/packages/nanoFramework.Tough/$(MY_NUGET_VERSION)) v$(MY_NUGET_VERSION)' assets: '$(Build.ArtifactStagingDirectory)/$(nugetPackageName).$(MY_NUGET_VERSION).nupkg' isPreRelease: false addChangeLog: false diff --git a/nanoFramework.M5Stack.sln b/nanoFramework.M5Stack.sln index 42692411..40b4c21f 100644 --- a/nanoFramework.M5Stack.sln +++ b/nanoFramework.M5Stack.sln @@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution nanoFramework.AtomLite.nuspec = nanoFramework.AtomLite.nuspec nanoFramework.AtomMatrix.nuspec = nanoFramework.AtomMatrix.nuspec nanoFramework.M5CoreS3.nuspec = nanoFramework.M5CoreS3.nuspec + nanoFramework.M5StickS3.nuspec = nanoFramework.M5StickS3.nuspec nanoFramework.M5Core.nuspec = nanoFramework.M5Core.nuspec nanoFramework.M5Core2.nuspec = nanoFramework.M5Core2.nuspec nanoFramework.M5StickC.nuspec = nanoFramework.M5StickC.nuspec @@ -64,6 +65,10 @@ Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "nanoFramework.M5CoreS3", "n EndProject Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "M5CoreS3TestApp", "Tests\M5CoreS3TestApp\M5CoreS3TestApp.nfproj", "{B3A0FB0D-44A3-4B09-A9A7-DF8BA963A4C1}" EndProject +Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "nanoFramework.M5StickS3", "nanoFramework.M5StickS3\nanoFramework.M5StickS3.nfproj", "{AA95E2BF-4A10-4F9A-8D2D-C7362A8C3562}" +EndProject +Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "M5StickS3TestApp", "Tests\M5StickS3TestApp\M5StickS3TestApp.nfproj", "{3AC12F12-B6DA-414F-8E30-DF57F5334B2D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -178,6 +183,18 @@ Global {B3A0FB0D-44A3-4B09-A9A7-DF8BA963A4C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {B3A0FB0D-44A3-4B09-A9A7-DF8BA963A4C1}.Release|Any CPU.Build.0 = Release|Any CPU {B3A0FB0D-44A3-4B09-A9A7-DF8BA963A4C1}.Release|Any CPU.Deploy.0 = Release|Any CPU + {AA95E2BF-4A10-4F9A-8D2D-C7362A8C3562}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA95E2BF-4A10-4F9A-8D2D-C7362A8C3562}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA95E2BF-4A10-4F9A-8D2D-C7362A8C3562}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {AA95E2BF-4A10-4F9A-8D2D-C7362A8C3562}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA95E2BF-4A10-4F9A-8D2D-C7362A8C3562}.Release|Any CPU.Build.0 = Release|Any CPU + {AA95E2BF-4A10-4F9A-8D2D-C7362A8C3562}.Release|Any CPU.Deploy.0 = Release|Any CPU + {3AC12F12-B6DA-414F-8E30-DF57F5334B2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3AC12F12-B6DA-414F-8E30-DF57F5334B2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AC12F12-B6DA-414F-8E30-DF57F5334B2D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {3AC12F12-B6DA-414F-8E30-DF57F5334B2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3AC12F12-B6DA-414F-8E30-DF57F5334B2D}.Release|Any CPU.Build.0 = Release|Any CPU + {3AC12F12-B6DA-414F-8E30-DF57F5334B2D}.Release|Any CPU.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -195,6 +212,7 @@ Global {5FF00F7C-8ED8-4468-9959-497CE8C5B1AF} = {5972CDE6-43B4-42F0-9276-6B70B7EF6437} {F5BF3DA4-B92E-46C6-A135-37E6A7E002A1} = {5972CDE6-43B4-42F0-9276-6B70B7EF6437} {B3A0FB0D-44A3-4B09-A9A7-DF8BA963A4C1} = {5972CDE6-43B4-42F0-9276-6B70B7EF6437} + {3AC12F12-B6DA-414F-8E30-DF57F5334B2D} = {5972CDE6-43B4-42F0-9276-6B70B7EF6437} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DD82D7FF-B798-48A4-8506-2FBA0001D32F} diff --git a/nanoFramework.M5StickS3.nuspec b/nanoFramework.M5StickS3.nuspec new file mode 100644 index 00000000..fe215bc1 --- /dev/null +++ b/nanoFramework.M5StickS3.nuspec @@ -0,0 +1,41 @@ + + + + nanoFramework.M5StickS3 + $version$ + nanoFramework.M5StickS3 + nanoframework + false + LICENSE.md + + + docs\README.md + false + https://github.com/nanoframework/nanoFramework.M5Stack + images\nf-logo.png + + Copyright (c) .NET Foundation and Contributors + This package includes the nanoFramework.M5StickS3 assembly for .NET nanoFramework C# projects. + nanoFramework C# csharp netmf netnf m5stack M5StickS3 + + + + + + + + + + + + + + + + + + + + + + diff --git a/nanoFramework.M5StickS3/M5StickS3.cs b/nanoFramework.M5StickS3/M5StickS3.cs new file mode 100644 index 00000000..87078bf4 --- /dev/null +++ b/nanoFramework.M5StickS3/M5StickS3.cs @@ -0,0 +1,218 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Iot.Device.Bmi270; +using Iot.Device.Button; +using Iot.Device.M5Pm1; +using nanoFramework.Hardware.Esp32; +using nanoFramework.Runtime.Native; +using System.Device.Gpio; +using System.Device.I2c; +using System.Diagnostics; +using UnitsNet; + +namespace nanoFramework.M5Stack +{ + /// + /// M5StickS3 board support. + /// + public static class M5StickS3 + { + /// + /// The internal I2C bus ID for the PMIC and accelerometer/gyroscope. + /// + public const int InternalI2cBusId = 1; + + private const int InternalI2cSdaPin = 47; + private const int InternalI2cSclPin = 48; + + /// + /// The internal I2C bus ID for the Grove connector. + /// + public const int GroveI2cBusId = 2; + + /// + /// The Grove connector SDA pin. + /// + public const int GroveI2cSdaPin = 9; + + /// + /// The Grove connector SCL pin. + /// + public const int GroveI2cSclPin = 10; + + private const int ButtonAPin = 11; + private const int ButtonBPin = 12; + + private static M5Pm1 _pmic; + private static GpioController _gpio; + private static GpioButton _buttonA; + private static GpioButton _buttonB; + private static Bmi270AccelerometerGyroscope _accelerometerGyroscope; + private static Screen _screen; + + static M5StickS3() + { + Configuration.SetPinFunction(InternalI2cSdaPin, DeviceFunction.I2C1_DATA); + Configuration.SetPinFunction(InternalI2cSclPin, DeviceFunction.I2C1_CLOCK); + Configuration.SetPinFunction(GroveI2cSdaPin, DeviceFunction.I2C2_DATA); + Configuration.SetPinFunction(GroveI2cSclPin, DeviceFunction.I2C2_CLOCK); + + // M5PM1 requires a 100 kHz I2C bus on M5StickS3. + I2cConnectionSettings pmicSettings = new I2cConnectionSettings(InternalI2cBusId, M5Pm1.I2cDefaultAddress, I2cBusSpeed.StandardMode); + _pmic = new M5Pm1(new I2cDevice(pmicSettings)); + int deviceId = _pmic.GetDeviceId(); + Debug.WriteLine($"M5PM1 device ID: 0x{deviceId:X4} (expected 0x{M5Pm1.DeviceId:X4})."); + InitializePmic(); + } + + /// + /// Gets the main GPIO controller. + /// + public static GpioController GpioController + { + get + { + if (_gpio == null) + { + _gpio = new GpioController(); + } + + return _gpio; + } + } + + /// + /// Gets the button below the display. + /// + public static GpioButton ButtonA + { + get + { + if (_buttonA == null) + { + _buttonA = new GpioButton(ButtonAPin, GpioController, false); + } + + return _buttonA; + } + } + + /// + /// Gets the side button. + /// + public static GpioButton ButtonB + { + get + { + if (_buttonB == null) + { + _buttonB = new GpioButton(ButtonBPin, GpioController, false); + } + + return _buttonB; + } + } + + /// + /// Gets the onboard accelerometer and gyroscope. + /// + public static Bmi270AccelerometerGyroscope AccelerometerGyroscope + { + get + { + if (_accelerometerGyroscope == null) + { + // Important: keep standard mode because of the M5PM1 + _accelerometerGyroscope = new Bmi270AccelerometerGyroscope( + new I2cDevice(new I2cConnectionSettings( + InternalI2cBusId, + Bmi270AccelerometerGyroscope.DefaultI2cAddress, + I2cBusSpeed.StandardMode))); + } + + return _accelerometerGyroscope; + } + } + + /// + /// Reads the battery voltage in millivolts from the PMIC. + /// + public static ElectricPotential BatteryVoltage => _pmic.GetBatteryVoltage(); + + /// + /// Reads the VBUS voltage in millivolts from the PMIC. + /// + public static ElectricPotential VBusVoltage => _pmic.GetVbusVoltage(); + + /// + /// Gets whether charging is currently active. + /// + public static bool IsCharging => _pmic.IsCharging; + + /// + /// Gets or sets the 5V output state. + /// + public static bool External5VEnabled + { + get => _pmic.ExternalOutputEnabled; + set => _pmic.ExternalOutputEnabled = value; + } + + /// + /// Gets the Power Management device. + /// + public static M5Pm1 Power => _pmic; + + /// + /// Initializes the display. + /// + /// Bitmap allocation in bytes. + public static void InitializeScreen(int memoryBitMapAllocation = Screen.DefaultMemoryAllocationBitmap) + { + if (_screen == null) + { + try + { + // PM1_G2 powers the LCD path. + _pmic.SetGpioFunction(Pin.Gpio2, GpioFunction.Gpio); + _pmic.SetGpioDrive(Pin.Gpio2, GpioDrive.PushPull); + _pmic.SetGpioMode(Pin.Gpio2, PinMode.Output); + _pmic.WriteGpio(Pin.Gpio2, PinValue.High); + _screen = new Screen(memoryBitMapAllocation); + } + catch (System.Exception ex) + { + Debug.WriteLine($"M5StickS3.InitializeScreen failed: {ex}"); + throw; + } + } + } + + /// + /// Gets a device on the internal I2C bus. + /// + public static I2cDevice GetI2cDevice(int i2cDeviceAddress) => new I2cDevice(new I2cConnectionSettings(InternalI2cBusId, i2cDeviceAddress)); + + /// + /// Gets a device on the Grove I2C bus. + /// + public static I2cDevice GetGrove(int i2cDeviceAddress) => new I2cDevice(new I2cConnectionSettings(GroveI2cBusId, i2cDeviceAddress)); + + internal static void SetLcdPower(bool enabled) + { + _pmic.WriteGpio(Pin.Gpio2, enabled); + } + + private static void InitializePmic() + { + // Constructor wake-up already applies the reliability init (I2C sleep off, watchdog off). + + // PM1_G3 is used by vendor firmware to gate the audio PA. + _pmic.SetGpioFunction(Pin.Gpio3, GpioFunction.Gpio); + _pmic.SetGpioDrive(Pin.Gpio3, GpioDrive.PushPull); + _pmic.SetGpioMode(Pin.Gpio3, PinMode.Output); + _pmic.WriteGpio(Pin.Gpio3, PinValue.Low); + } + } +} diff --git a/nanoFramework.M5StickS3/Properties/AssemblyInfo.cs b/nanoFramework.M5StickS3/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..008bc3bd --- /dev/null +++ b/nanoFramework.M5StickS3/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("nanoFramework.M5StickS3")] +[assembly: AssemblyCompany("nanoFramework Contributors")] +[assembly: AssemblyCopyright("Copyright(c).NET Foundation and Contributors")] + +[assembly: ComVisible(false)] diff --git a/nanoFramework.M5StickS3/Screen.cs b/nanoFramework.M5StickS3/Screen.cs new file mode 100644 index 00000000..f28a8e50 --- /dev/null +++ b/nanoFramework.M5StickS3/Screen.cs @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using nanoFramework.Hardware.Esp32; +using nanoFramework.UI; +using System.Device.Gpio; +using System.Threading; + +namespace nanoFramework.M5Stack +{ + /// + /// M5StickS3 screen support. + /// + public class Screen : ScreenBase + { + /// + /// Default memory allocation for bitmap in bytes. + /// + public const int DefaultMemoryAllocationBitmap = 1024; + + private const int SpiBus = 1; + private const int ChipSelect = 41; + private const int DataCommand = 45; + private const int Reset = 21; + private const int NoResetInNativeDriver = -1; + private const int DummyMisoPin = 42; + private const int BackLight = 38; + private const int Width = 135; + private const int Height = 240; + private const int OffsetX = 52; + private const int OffsetY = 40; + private const int ScreenMiso = 39; + private const int ScreenClock = 40; + + private static bool _isInitialized; + private static bool _isEnabled; + + /// + /// Creates and initializes the screen. + /// + /// Bitmap allocation in bytes. + public Screen(int memoryBitMapAllocation = DefaultMemoryAllocationBitmap) + { + if (_isInitialized) + { + return; + } + + Configuration.SetPinFunction(ScreenMiso, DeviceFunction.SPI1_MOSI); + Configuration.SetPinFunction(ScreenClock, DeviceFunction.SPI1_CLOCK); + Configuration.SetPinFunction(DummyMisoPin, DeviceFunction.SPI1_MISO); + + Controller = M5StickS3.GpioController; + if (!Controller.IsPinOpen(Reset)) + { + Controller.OpenPin(Reset, PinMode.Output); + } + + if (!Controller.IsPinOpen(BackLight)) + { + Controller.OpenPin(BackLight, PinMode.Output); + } + + M5StickS3.SetLcdPower(true); + Controller.Write(BackLight, PinValue.High); + Thread.Sleep(100); + + // Pulse LCD reset explicitly to avoid native reset-path instability on some S3 firmwares. + Controller.Write(Reset, PinValue.Low); + Thread.Sleep(10); + Controller.Write(Reset, PinValue.High); + Thread.Sleep(100); + + uint actualBufferSize = DisplayControl.Initialize( + new SpiConfiguration(SpiBus, ChipSelect, DataCommand, NoResetInNativeDriver, -1), + new ScreenConfiguration(OffsetX, OffsetY, Width, Height, StickS3St7789.GraphicDriver), + (uint)memoryBitMapAllocation); + + MemoryAllocationBitmap = (int)actualBufferSize; + ScreenBase.BackLightPin = BackLight; + Enabled = true; + _isInitialized = true; + } + + /// + /// Enables or disables the screen. + /// + public new static bool Enabled + { + get => _isEnabled; + set + { + _isEnabled = value; + IsEnabled = value; + + if (value) + { + M5StickS3.SetLcdPower(true); + Controller.Write(BackLight, PinValue.High); + Thread.Sleep(100); + } + else + { + Thread.Sleep(2); + Controller.Write(BackLight, PinValue.Low); + M5StickS3.SetLcdPower(false); + } + } + } + + /// + /// Sets luminosity in percentage. + /// + public new static byte LuminosityPercentage + { + get => (byte)(_isEnabled ? 100 : 0); + set => Enabled = value > 0; + } + } +} diff --git a/nanoFramework.M5StickS3/StickS3St7789.cs b/nanoFramework.M5StickS3/StickS3St7789.cs new file mode 100644 index 00000000..ea6dfa07 --- /dev/null +++ b/nanoFramework.M5StickS3/StickS3St7789.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using nanoFramework.UI; + +namespace nanoFramework.M5Stack +{ + /// + /// M5StickS3-specific managed ST7789 driver. + /// + internal static class StickS3St7789 + { + private static GraphicDriver _driver; + + private enum St7789Command : byte + { + SleepOut = 0x11, + DisplayOn = 0x29, + ColumnAddressSet = 0x2A, + RowAddressSet = 0x2B, + MemoryWrite = 0x2C, + MemoryAccessControl = 0x36, + ColorMode = 0x3A, + InversionOn = 0x21, + } + + private enum Orientation : byte + { + My = 0x80, + Mx = 0x40, + Mv = 0x20, + Bgr = 0x08, + } + + public static GraphicDriver GraphicDriver + { + get + { + if (_driver == null) + { + _driver = new GraphicDriver() + { + BitsPerPixel = 16, + MemoryWrite = (byte)St7789Command.MemoryWrite, + SetColumnAddress = (byte)St7789Command.ColumnAddressSet, + SetRowAddress = (byte)St7789Command.RowAddressSet, + InitializationSequence = new byte[] + { + (byte)GraphicDriverCommandType.Command, 2, (byte)St7789Command.ColorMode, 0x55, + (byte)GraphicDriverCommandType.Command, 1, (byte)St7789Command.InversionOn, + (byte)GraphicDriverCommandType.Command, 1, (byte)St7789Command.SleepOut, + (byte)GraphicDriverCommandType.Sleep, 12, + (byte)GraphicDriverCommandType.Command, 1, (byte)St7789Command.DisplayOn, + }, + OrientationLandscape = new byte[] + { + (byte)GraphicDriverCommandType.Command, 2, (byte)St7789Command.MemoryAccessControl, (byte)Orientation.Bgr, + }, + OrientationPortrait = new byte[] + { + (byte)GraphicDriverCommandType.Command, 2, (byte)St7789Command.MemoryAccessControl, (byte)(Orientation.Mv | Orientation.Bgr), + }, + OrientationLandscape180 = new byte[] + { + (byte)GraphicDriverCommandType.Command, 2, (byte)St7789Command.MemoryAccessControl, (byte)(Orientation.Mx | Orientation.Bgr), + }, + OrientationPortrait180 = new byte[] + { + (byte)GraphicDriverCommandType.Command, 2, (byte)St7789Command.MemoryAccessControl, (byte)(Orientation.Mx | Orientation.My | Orientation.Mv | Orientation.Bgr), + }, + DefaultOrientation = DisplayOrientation.Landscape, + SetWindowType = SetWindowType.X16bitsY16Bit, + }; + } + + return _driver; + } + } + } +} diff --git a/nanoFramework.M5StickS3/nanoFramework.M5StickS3.nfproj b/nanoFramework.M5StickS3/nanoFramework.M5StickS3.nfproj new file mode 100644 index 00000000..7c0cd5b6 --- /dev/null +++ b/nanoFramework.M5StickS3/nanoFramework.M5StickS3.nfproj @@ -0,0 +1,123 @@ + + + + + $(MSBuildExtensionsPath)\nanoFramework\v1.0\ + + + + Debug + AnyCPU + {11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {AA95E2BF-4A10-4F9A-8D2D-C7362A8C3562} + Library + Properties + 512 + nanoFramework.M5StickS3 + nanoFramework.M5StickS3 + v1.0 + bin\$(Configuration)\nanoFramework.M5StickS3.xml + + + + + + + + + + + + + + ..\packages\nanoFramework.Iot.Device.Bmm150.1.2.991\lib\Iot.Device.Bmm150.dll + + + ..\packages\nanoFramework.Iot.Device.Button.1.2.1002\lib\Iot.Device.Button.dll + + + ..\packages\nanoFramework.Iot.Device.Bmi270.1.0.9\lib\Iot.Device.Bmi270.dll + + + ..\packages\nanoFramework.Iot.Device.Common.NumberHelper.1.2.862\lib\Iot.Device.Common.NumberHelper.dll + + + ..\packages\nanoFramework.Iot.Device.M5Pm1.1.0.1\lib\Iot.Device.M5Pm1.dll + + + ..\packages\nanoFramework.CoreLibrary.1.17.12\lib\mscorlib.dll + + + ..\packages\nanoFramework.Graphics.1.2.53\lib\nanoFramework.Graphics.dll + + + ..\packages\nanoFramework.Graphics.1.2.53\lib\nanoFramework.Graphics.Core.dll + + + ..\packages\nanoFramework.Hardware.Esp32.1.6.40\lib\nanoFramework.Hardware.Esp32.dll + + + ..\packages\nanoFramework.ResourceManager.1.2.32\lib\nanoFramework.ResourceManager.dll + + + ..\packages\nanoFramework.Runtime.Events.1.11.37\lib\nanoFramework.Runtime.Events.dll + + + ..\packages\nanoFramework.Runtime.Native.1.7.11\lib\nanoFramework.Runtime.Native.dll + + + ..\packages\nanoFramework.System.Collections.1.5.75\lib\nanoFramework.System.Collections.dll + + + ..\packages\nanoFramework.UnitsNet.Temperature.5.76.15\lib\netnano1.0\nanoFramework.UnitsNet.Temperature.dll + + + ..\packages\nanoFramework.UnitsNet.ElectricPotential.5.76.15\lib\netnano1.0\nanoFramework.UnitsNet.ElectricPotential.dll + + + ..\packages\nanoFramework.System.Buffers.Binary.BinaryPrimitives.1.2.862\lib\System.Buffers.Binary.BinaryPrimitives.dll + + + ..\packages\nanoFramework.System.Device.Gpio.1.1.62\lib\System.Device.Gpio.dll + + + ..\packages\nanoFramework.System.Device.I2c.1.1.29\lib\System.Device.I2c.dll + + + ..\packages\nanoFramework.System.Device.Model.1.2.862\lib\System.Device.Model.dll + + + ..\packages\nanoFramework.System.Diagnostics.Stopwatch.1.2.862\lib\System.Diagnostics.Stopwatch.dll + + + ..\packages\nanoFramework.System.Math.1.5.116\lib\System.Math.dll + + + ..\packages\nanoFramework.System.Numerics.1.2.862\lib\System.Numerics.dll + + + ..\packages\nanoFramework.System.Threading.1.1.52\lib\System.Threading.dll + + + ..\packages\UnitsNet.nanoFramework.Temperature.5.75.0\lib\UnitsNet.Temperature.dll + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/nanoFramework.M5StickS3/packages.config b/nanoFramework.M5StickS3/packages.config new file mode 100644 index 00000000..73f59b9a --- /dev/null +++ b/nanoFramework.M5StickS3/packages.config @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/nanoFramework.M5StickS3/packages.lock.json b/nanoFramework.M5StickS3/packages.lock.json new file mode 100644 index 00000000..0f14bd8f --- /dev/null +++ b/nanoFramework.M5StickS3/packages.lock.json @@ -0,0 +1,151 @@ +{ + "version": 1, + "dependencies": { + ".NETnanoFramework,Version=v1.0": { + "nanoFramework.CoreLibrary": { + "type": "Direct", + "requested": "[1.17.12, 1.17.12]", + "resolved": "1.17.12", + "contentHash": "j0fDBm74I7ORpskJTW1R9JgKqlmpgkQaB+ccBbRJcUKD1DGYlPDI1N4/kFwYOZrn8YjHUVFVv6n/1vg822ZSsw==" + }, + "nanoFramework.Graphics": { + "type": "Direct", + "requested": "[1.2.53, 1.2.53]", + "resolved": "1.2.53", + "contentHash": "LwXK03HlpMLH37Ol67GxQbjNksNXZE0pIqVZiol9ds+V3698PLyYcP/XayhTmu9pOntcDtmYL64jKuUr2dEu8g==" + }, + "nanoFramework.Hardware.Esp32": { + "type": "Direct", + "requested": "[1.6.40, 1.6.40]", + "resolved": "1.6.40", + "contentHash": "jq6wGAXuC5tvkmKaQX1W5NW/IoPLhosASAKbeA5QPDHX35AwOK/l9OZrc8jvgv52DiiXj/WtvKwgPj1W0ocNCQ==" + }, + "nanoFramework.Iot.Device.Bmi270": { + "type": "Direct", + "requested": "[1.0.9, 1.0.9]", + "resolved": "1.0.9", + "contentHash": "yG01UK1LH66dHOfic1nMJiQKRR2a7yNi/T9QGPaPpwmRNapIw1tjizCmZv/5pZbIhGtLgIl7EyazYVTbxLzQ6A==" + }, + "nanoFramework.Iot.Device.Bmm150": { + "type": "Direct", + "requested": "[1.2.991, 1.2.991]", + "resolved": "1.2.991", + "contentHash": "/xgUJPjbTQMM4ei94cr/s46WvU1+1zBJwGicUnwSQPXNEZBG30iS4osaAnc4J3owc9Y0U2EYkCFX01/0V8HDAg==" + }, + "nanoFramework.Iot.Device.Button": { + "type": "Direct", + "requested": "[1.2.1002, 1.2.1002]", + "resolved": "1.2.1002", + "contentHash": "AG4810GRfXKihn97fAdsQOYneHLNQzl+xHuDcS/JQhNJLbjhpkAzl85k9HO8Tjz1ajWtpUACib8lJSo+l4l3AA==" + }, + "nanoFramework.Iot.Device.Common.NumberHelper": { + "type": "Direct", + "requested": "[1.2.862, 1.2.862]", + "resolved": "1.2.862", + "contentHash": "Ax67ee3vmdfH2NdqtdFYawBzxWH5+KsUIvwnTlGHbXR3ko4t5a4mVelEqB4BheJZETRyNMqKcUGWdWFAbO2byQ==" + }, + "nanoFramework.Iot.Device.M5Pm1": { + "type": "Direct", + "requested": "[1.0.1, 1.0.1]", + "resolved": "1.0.1", + "contentHash": "aK4T8ojPfvG7T76s/nN5PzyyBOTKvvbDZ03TeRmRFwluCd+2e1pwj8YO/7o6cX0RJjyMIF02Qyx7O7usC17hEg==" + }, + "nanoFramework.ResourceManager": { + "type": "Direct", + "requested": "[1.2.32, 1.2.32]", + "resolved": "1.2.32", + "contentHash": "hpBiZ7yx5AnT/0T37cr2ySQwQDLKP+KaWu+O7ln9J43M/WaKFSCZFVsqXeJGby3/JDfm1cxhfOaQ1MMsevrdiA==" + }, + "nanoFramework.Runtime.Events": { + "type": "Direct", + "requested": "[1.11.37, 1.11.37]", + "resolved": "1.11.37", + "contentHash": "whTgEJCGpE8eGK7Z2fI6gTqsVeVrnCzP1KDSqIB2PY0/Jyw7NcnwCFe3s/43ch75DuAZt8/jTX0VJoJtN0Colw==" + }, + "nanoFramework.Runtime.Native": { + "type": "Direct", + "requested": "[1.7.11, 1.7.11]", + "resolved": "1.7.11", + "contentHash": "XPSTltZ9KeBruogVmjQpCphi1nLoJH49mpyp2eGBs8BTjKuL5TkMO20MoI8r73F/PW5AppTq49HvIZZavU5nPQ==" + }, + "nanoFramework.System.Buffers.Binary.BinaryPrimitives": { + "type": "Direct", + "requested": "[1.2.862, 1.2.862]", + "resolved": "1.2.862", + "contentHash": "O+jmbbw2h2cjCTShsvWCzjqn/kmRacJE4Ena9gJt5xF5D7XHyYh/uE/qbZ4LhlmlN6JCZjLRNiEfHyM1YUjyxg==" + }, + "nanoFramework.System.Collections": { + "type": "Direct", + "requested": "[1.5.75, 1.5.75]", + "resolved": "1.5.75", + "contentHash": "OiMpKo8RWbjZq1594YjdRnOGNN2aT4nowa+ET7scK0frGCux0a8LGaPkG56mmSfBCKzLlVs13LIlla3XLhXaMA==" + }, + "nanoFramework.System.Device.Gpio": { + "type": "Direct", + "requested": "[1.1.62, 1.1.62]", + "resolved": "1.1.62", + "contentHash": "BaSX4efW1WtqVBoqqKs3tb9y3QSZiIPcWyalbxFY7ovQ2FKoyAHI/RzTGjzPtSf3mUwmrvXnPowDida5NY5Urg==" + }, + "nanoFramework.System.Device.I2c": { + "type": "Direct", + "requested": "[1.1.29, 1.1.29]", + "resolved": "1.1.29", + "contentHash": "q8duNVWeK3RCiTmTZiO1UWr7s9HMsLnKWm5utxy9OQWeQtlh00l5CoFKxjot42s1NY5zLmfq7p7oWM/fgNEtlA==" + }, + "nanoFramework.System.Device.Model": { + "type": "Direct", + "requested": "[1.2.862, 1.2.862]", + "resolved": "1.2.862", + "contentHash": "dARhyb8BHkje2P+7VphJpRLynVhMkC0U4pFNecLs6jw8DHRMMJyszcbgo/N0ALeYjilNk9hea+wzBLFrgZpsUQ==" + }, + "nanoFramework.System.Diagnostics.Stopwatch": { + "type": "Direct", + "requested": "[1.2.862, 1.2.862]", + "resolved": "1.2.862", + "contentHash": "auNXXoJYOJ7mAxPvMPhbxFbDCtfKF/wSdsoBEQiRd8NaQWmLg12JL6YbNiKiNs+fBlxAL6duUorBdhSyPCdayg==" + }, + "nanoFramework.System.Math": { + "type": "Direct", + "requested": "[1.5.116, 1.5.116]", + "resolved": "1.5.116", + "contentHash": "0S05UKcoI9Ukz9cMNPC9huFW9vSL42/4gIh4fw8ou8HkcFmTYb0OL8VF2Ah94hEDW5mqcYAVr0ipuGWPzIRqaA==" + }, + "nanoFramework.System.Numerics": { + "type": "Direct", + "requested": "[1.2.862, 1.2.862]", + "resolved": "1.2.862", + "contentHash": "YdYhLCoj5i77e4f0qw1a45GxDAFYR2F7zLlrUDdUE12JGweBR0kruURKrGJMl4uYWkORfO18PxyqNOXvSqKKCg==" + }, + "nanoFramework.System.Threading": { + "type": "Direct", + "requested": "[1.1.52, 1.1.52]", + "resolved": "1.1.52", + "contentHash": "kv+US/+7QKV1iT/snxBh032vwZ+3krJ4vujlSsvmS2nNj/nK64R3bq/ST3bCFquxHDD0mog8irtCBCsFazr4kA==" + }, + "nanoFramework.UnitsNet.ElectricPotential": { + "type": "Direct", + "requested": "[5.76.15, 5.76.15]", + "resolved": "5.76.15", + "contentHash": "amhs/PAWNx7+g5JEaG1V3wB/324NJ93RdY8MztR38yXvDppk/qFx+y2Ncno+8GwVRy4GHYskIbMMSHsHqOf9VQ==" + }, + "nanoFramework.UnitsNet.Temperature": { + "type": "Direct", + "requested": "[5.76.15, 5.76.15]", + "resolved": "5.76.15", + "contentHash": "rmgtqp2kdwd1LkWCHy/iabt4HFHPgTSllCc8ex8M1xJ/2n3CH+Udng7M/wbtMP5II2yF5zMqxowzQ2iKOaoX/g==" + }, + "Nerdbank.GitVersioning": { + "type": "Direct", + "requested": "[3.10.85, 3.10.85]", + "resolved": "3.10.85", + "contentHash": "smhqiTAbnI/6NRn+k7m+a65qKY7nREbnFqFgqxiw3JS8kXiBUOxdCxIZFP9TOpCsoh46DbMlBlcd+dWQYAVVCA==" + }, + "UnitsNet.nanoFramework.Temperature": { + "type": "Direct", + "requested": "[5.75.0, 5.75.0]", + "resolved": "5.75.0", + "contentHash": "SpA9Z7T1sNaR7YBCYp+L9/ei1bjDkqi8PyPfOWVBjkWInuPSOQaPG9Y7UttIpJ0M2y1GTrCG1kmRCUewGiSFAg==" + } + } + } +} \ No newline at end of file From 8f559e6f94a7c341e65cc18ae15b59e39adff271 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 10 Jul 2026 16:29:53 +0200 Subject: [PATCH 2/9] removing version --- Tests/M5StickS3TestApp/Properties/AssemblyInfo.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Tests/M5StickS3TestApp/Properties/AssemblyInfo.cs b/Tests/M5StickS3TestApp/Properties/AssemblyInfo.cs index a6dc39c4..d3f4d14c 100644 --- a/Tests/M5StickS3TestApp/Properties/AssemblyInfo.cs +++ b/Tests/M5StickS3TestApp/Properties/AssemblyInfo.cs @@ -14,6 +14,3 @@ [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] From 4acbd26bdd757ca90b26ee5ff2a5158687c0096c Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 10 Jul 2026 16:34:01 +0200 Subject: [PATCH 3/9] removing version coreS3 --- Tests/M5CoreS3TestApp/Properties/AssemblyInfo.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Tests/M5CoreS3TestApp/Properties/AssemblyInfo.cs b/Tests/M5CoreS3TestApp/Properties/AssemblyInfo.cs index d1e3ef1c..44092555 100644 --- a/Tests/M5CoreS3TestApp/Properties/AssemblyInfo.cs +++ b/Tests/M5CoreS3TestApp/Properties/AssemblyInfo.cs @@ -11,6 +11,3 @@ [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file From 53ceb24513535987661ef1a6de22ebf4c83aa149 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 10 Jul 2026 17:04:13 +0200 Subject: [PATCH 4/9] adding missing package json --- .../M5StickS3TestApp/M5StickS3TestApp.nfproj | 3 + Tests/M5StickS3TestApp/packages.lock.json | 145 ++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 Tests/M5StickS3TestApp/packages.lock.json diff --git a/Tests/M5StickS3TestApp/M5StickS3TestApp.nfproj b/Tests/M5StickS3TestApp/M5StickS3TestApp.nfproj index ac076324..e97c8458 100644 --- a/Tests/M5StickS3TestApp/M5StickS3TestApp.nfproj +++ b/Tests/M5StickS3TestApp/M5StickS3TestApp.nfproj @@ -101,6 +101,9 @@ + + + diff --git a/Tests/M5StickS3TestApp/packages.lock.json b/Tests/M5StickS3TestApp/packages.lock.json new file mode 100644 index 00000000..ddc6878a --- /dev/null +++ b/Tests/M5StickS3TestApp/packages.lock.json @@ -0,0 +1,145 @@ +{ + "version": 1, + "dependencies": { + ".NETnanoFramework,Version=v1.0": { + "nanoFramework.CoreLibrary": { + "type": "Direct", + "requested": "[1.17.12, 1.17.12]", + "resolved": "1.17.12", + "contentHash": "j0fDBm74I7ORpskJTW1R9JgKqlmpgkQaB+ccBbRJcUKD1DGYlPDI1N4/kFwYOZrn8YjHUVFVv6n/1vg822ZSsw==" + }, + "nanoFramework.Graphics": { + "type": "Direct", + "requested": "[1.2.53, 1.2.53]", + "resolved": "1.2.53", + "contentHash": "LwXK03HlpMLH37Ol67GxQbjNksNXZE0pIqVZiol9ds+V3698PLyYcP/XayhTmu9pOntcDtmYL64jKuUr2dEu8g==" + }, + "nanoFramework.Hardware.Esp32": { + "type": "Direct", + "requested": "[1.6.40, 1.6.40]", + "resolved": "1.6.40", + "contentHash": "jq6wGAXuC5tvkmKaQX1W5NW/IoPLhosASAKbeA5QPDHX35AwOK/l9OZrc8jvgv52DiiXj/WtvKwgPj1W0ocNCQ==" + }, + "nanoFramework.Iot.Device.Bmi270": { + "type": "Direct", + "requested": "[1.0.9, 1.0.9]", + "resolved": "1.0.9", + "contentHash": "yG01UK1LH66dHOfic1nMJiQKRR2a7yNi/T9QGPaPpwmRNapIw1tjizCmZv/5pZbIhGtLgIl7EyazYVTbxLzQ6A==" + }, + "nanoFramework.Iot.Device.Bmm150": { + "type": "Direct", + "requested": "[1.2.991, 1.2.991]", + "resolved": "1.2.991", + "contentHash": "/xgUJPjbTQMM4ei94cr/s46WvU1+1zBJwGicUnwSQPXNEZBG30iS4osaAnc4J3owc9Y0U2EYkCFX01/0V8HDAg==" + }, + "nanoFramework.Iot.Device.Button": { + "type": "Direct", + "requested": "[1.2.1002, 1.2.1002]", + "resolved": "1.2.1002", + "contentHash": "AG4810GRfXKihn97fAdsQOYneHLNQzl+xHuDcS/JQhNJLbjhpkAzl85k9HO8Tjz1ajWtpUACib8lJSo+l4l3AA==" + }, + "nanoFramework.Iot.Device.Common.NumberHelper": { + "type": "Direct", + "requested": "[1.2.862, 1.2.862]", + "resolved": "1.2.862", + "contentHash": "Ax67ee3vmdfH2NdqtdFYawBzxWH5+KsUIvwnTlGHbXR3ko4t5a4mVelEqB4BheJZETRyNMqKcUGWdWFAbO2byQ==" + }, + "nanoFramework.Iot.Device.M5Pm1": { + "type": "Direct", + "requested": "[1.0.1, 1.0.1]", + "resolved": "1.0.1", + "contentHash": "aK4T8ojPfvG7T76s/nN5PzyyBOTKvvbDZ03TeRmRFwluCd+2e1pwj8YO/7o6cX0RJjyMIF02Qyx7O7usC17hEg==" + }, + "nanoFramework.ResourceManager": { + "type": "Direct", + "requested": "[1.2.32, 1.2.32]", + "resolved": "1.2.32", + "contentHash": "hpBiZ7yx5AnT/0T37cr2ySQwQDLKP+KaWu+O7ln9J43M/WaKFSCZFVsqXeJGby3/JDfm1cxhfOaQ1MMsevrdiA==" + }, + "nanoFramework.Runtime.Events": { + "type": "Direct", + "requested": "[1.11.37, 1.11.37]", + "resolved": "1.11.37", + "contentHash": "whTgEJCGpE8eGK7Z2fI6gTqsVeVrnCzP1KDSqIB2PY0/Jyw7NcnwCFe3s/43ch75DuAZt8/jTX0VJoJtN0Colw==" + }, + "nanoFramework.Runtime.Native": { + "type": "Direct", + "requested": "[1.7.11, 1.7.11]", + "resolved": "1.7.11", + "contentHash": "XPSTltZ9KeBruogVmjQpCphi1nLoJH49mpyp2eGBs8BTjKuL5TkMO20MoI8r73F/PW5AppTq49HvIZZavU5nPQ==" + }, + "nanoFramework.System.Buffers.Binary.BinaryPrimitives": { + "type": "Direct", + "requested": "[1.2.862, 1.2.862]", + "resolved": "1.2.862", + "contentHash": "O+jmbbw2h2cjCTShsvWCzjqn/kmRacJE4Ena9gJt5xF5D7XHyYh/uE/qbZ4LhlmlN6JCZjLRNiEfHyM1YUjyxg==" + }, + "nanoFramework.System.Collections": { + "type": "Direct", + "requested": "[1.5.75, 1.5.75]", + "resolved": "1.5.75", + "contentHash": "OiMpKo8RWbjZq1594YjdRnOGNN2aT4nowa+ET7scK0frGCux0a8LGaPkG56mmSfBCKzLlVs13LIlla3XLhXaMA==" + }, + "nanoFramework.System.Device.Gpio": { + "type": "Direct", + "requested": "[1.1.62, 1.1.62]", + "resolved": "1.1.62", + "contentHash": "BaSX4efW1WtqVBoqqKs3tb9y3QSZiIPcWyalbxFY7ovQ2FKoyAHI/RzTGjzPtSf3mUwmrvXnPowDida5NY5Urg==" + }, + "nanoFramework.System.Device.I2c": { + "type": "Direct", + "requested": "[1.1.29, 1.1.29]", + "resolved": "1.1.29", + "contentHash": "q8duNVWeK3RCiTmTZiO1UWr7s9HMsLnKWm5utxy9OQWeQtlh00l5CoFKxjot42s1NY5zLmfq7p7oWM/fgNEtlA==" + }, + "nanoFramework.System.Device.Model": { + "type": "Direct", + "requested": "[1.2.862, 1.2.862]", + "resolved": "1.2.862", + "contentHash": "dARhyb8BHkje2P+7VphJpRLynVhMkC0U4pFNecLs6jw8DHRMMJyszcbgo/N0ALeYjilNk9hea+wzBLFrgZpsUQ==" + }, + "nanoFramework.System.Diagnostics.Stopwatch": { + "type": "Direct", + "requested": "[1.2.862, 1.2.862]", + "resolved": "1.2.862", + "contentHash": "auNXXoJYOJ7mAxPvMPhbxFbDCtfKF/wSdsoBEQiRd8NaQWmLg12JL6YbNiKiNs+fBlxAL6duUorBdhSyPCdayg==" + }, + "nanoFramework.System.Math": { + "type": "Direct", + "requested": "[1.5.116, 1.5.116]", + "resolved": "1.5.116", + "contentHash": "0S05UKcoI9Ukz9cMNPC9huFW9vSL42/4gIh4fw8ou8HkcFmTYb0OL8VF2Ah94hEDW5mqcYAVr0ipuGWPzIRqaA==" + }, + "nanoFramework.System.Numerics": { + "type": "Direct", + "requested": "[1.2.862, 1.2.862]", + "resolved": "1.2.862", + "contentHash": "YdYhLCoj5i77e4f0qw1a45GxDAFYR2F7zLlrUDdUE12JGweBR0kruURKrGJMl4uYWkORfO18PxyqNOXvSqKKCg==" + }, + "nanoFramework.System.Threading": { + "type": "Direct", + "requested": "[1.1.52, 1.1.52]", + "resolved": "1.1.52", + "contentHash": "kv+US/+7QKV1iT/snxBh032vwZ+3krJ4vujlSsvmS2nNj/nK64R3bq/ST3bCFquxHDD0mog8irtCBCsFazr4kA==" + }, + "nanoFramework.UnitsNet.ElectricPotential": { + "type": "Direct", + "requested": "[5.76.15, 5.76.15]", + "resolved": "5.76.15", + "contentHash": "amhs/PAWNx7+g5JEaG1V3wB/324NJ93RdY8MztR38yXvDppk/qFx+y2Ncno+8GwVRy4GHYskIbMMSHsHqOf9VQ==" + }, + "nanoFramework.UnitsNet.Temperature": { + "type": "Direct", + "requested": "[5.76.15, 5.76.15]", + "resolved": "5.76.15", + "contentHash": "rmgtqp2kdwd1LkWCHy/iabt4HFHPgTSllCc8ex8M1xJ/2n3CH+Udng7M/wbtMP5II2yF5zMqxowzQ2iKOaoX/g==" + }, + "UnitsNet.nanoFramework.Temperature": { + "type": "Direct", + "requested": "[5.75.0, 5.75.0]", + "resolved": "5.75.0", + "contentHash": "SpA9Z7T1sNaR7YBCYp+L9/ei1bjDkqi8PyPfOWVBjkWInuPSOQaPG9Y7UttIpJ0M2y1GTrCG1kmRCUewGiSFAg==" + } + } + } +} \ No newline at end of file From b3105d14b161e2967fae4e2d34d558f8b40d98f6 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 10 Jul 2026 17:27:52 +0200 Subject: [PATCH 5/9] fixing nuspec nit --- nanoFramework.M5StickS3.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nanoFramework.M5StickS3.nuspec b/nanoFramework.M5StickS3.nuspec index fe215bc1..8518f4e7 100644 --- a/nanoFramework.M5StickS3.nuspec +++ b/nanoFramework.M5StickS3.nuspec @@ -18,7 +18,7 @@ This package includes the nanoFramework.M5StickS3 assembly for .NET nanoFramework C# projects. nanoFramework C# csharp netmf netnf m5stack M5StickS3 - + From 49cae9b73d72e98a6e77049590a2ae5b5a34e1bf Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 10 Jul 2026 17:34:02 +0200 Subject: [PATCH 6/9] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- nanoFramework.M5StickS3/Screen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nanoFramework.M5StickS3/Screen.cs b/nanoFramework.M5StickS3/Screen.cs index f28a8e50..a7796234 100644 --- a/nanoFramework.M5StickS3/Screen.cs +++ b/nanoFramework.M5StickS3/Screen.cs @@ -29,7 +29,7 @@ public class Screen : ScreenBase private const int Height = 240; private const int OffsetX = 52; private const int OffsetY = 40; - private const int ScreenMiso = 39; + private const int ScreenMosi = 39; private const int ScreenClock = 40; private static bool _isInitialized; @@ -46,7 +46,7 @@ public Screen(int memoryBitMapAllocation = DefaultMemoryAllocationBitmap) return; } - Configuration.SetPinFunction(ScreenMiso, DeviceFunction.SPI1_MOSI); + Configuration.SetPinFunction(ScreenMosi, DeviceFunction.SPI1_MOSI); Configuration.SetPinFunction(ScreenClock, DeviceFunction.SPI1_CLOCK); Configuration.SetPinFunction(DummyMisoPin, DeviceFunction.SPI1_MISO); From 4228edf6af9880a345eff2cc2e0b1bd7351fc076 Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 10 Jul 2026 17:34:17 +0200 Subject: [PATCH 7/9] improving sample --- Tests/M5StickS3TestApp/Program.cs | 126 ++++++++++++++++++------------ 1 file changed, 77 insertions(+), 49 deletions(-) diff --git a/Tests/M5StickS3TestApp/Program.cs b/Tests/M5StickS3TestApp/Program.cs index f98f4387..d6a77a2b 100644 --- a/Tests/M5StickS3TestApp/Program.cs +++ b/Tests/M5StickS3TestApp/Program.cs @@ -43,6 +43,11 @@ Debug.WriteLine($"Button B pressed, ext 5V enabled: {M5StickS3.External5VEnabled}"); }; +byte[][] glyphLookup = CreateGlyphLookup(); +const int telemetryWidth = 135; +ushort[] telemetryLineBuffer = null; +int telemetryLineBufferHeight = 0; + while (true) { var accel = M5StickS3.AccelerometerGyroscope.GetAccelerometer(); @@ -151,15 +156,22 @@ static string FitTelemetryText(string text) return text.Substring(0, maxChars); } -static void DrawTelemetryLine(ushort y, string text, Color foreground, Color background, int scale) +void DrawTelemetryLine(ushort y, string text, Color foreground, Color background, int scale) { - const int width = 135; + int width = telemetryWidth; int height = (7 * scale) + 1; int charWidth = (5 * scale) + 1; ushort fg = foreground.ToBgr565(); ushort bg = background.ToBgr565(); - ushort[] lineBuffer = new ushort[width * height]; + + if (telemetryLineBuffer == null || telemetryLineBufferHeight != height) + { + telemetryLineBuffer = new ushort[width * height]; + telemetryLineBufferHeight = height; + } + + ushort[] lineBuffer = telemetryLineBuffer; for (int i = 0; i < lineBuffer.Length; i++) { @@ -207,53 +219,69 @@ static void DrawTelemetryLine(ushort y, string text, Color foreground, Color bac Screen.Write(0, y, width, (ushort)height, lineBuffer); } -static byte[] GetGlyph(char c) +byte[] GetGlyph(char c) { - switch (c) + int index = c; + if (index >= 0 && index < glyphLookup.Length) { - case 'A': return new byte[] { 0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11 }; - case 'B': return new byte[] { 0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E }; - case 'C': return new byte[] { 0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E }; - case 'D': return new byte[] { 0x1E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E }; - case 'E': return new byte[] { 0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F }; - case 'F': return new byte[] { 0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10 }; - case 'G': return new byte[] { 0x0E, 0x11, 0x10, 0x13, 0x11, 0x11, 0x0E }; - case 'H': return new byte[] { 0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11 }; - case 'I': return new byte[] { 0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E }; - case 'J': return new byte[] { 0x01, 0x01, 0x01, 0x01, 0x11, 0x11, 0x0E }; - case 'K': return new byte[] { 0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11 }; - case 'L': return new byte[] { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F }; - case 'M': return new byte[] { 0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11 }; - case 'N': return new byte[] { 0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x11 }; - case 'O': return new byte[] { 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E }; - case 'P': return new byte[] { 0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10 }; - case 'Q': return new byte[] { 0x0E, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0D }; - case 'R': return new byte[] { 0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11 }; - case 'S': return new byte[] { 0x0F, 0x10, 0x10, 0x0E, 0x01, 0x01, 0x1E }; - case 'T': return new byte[] { 0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; - case 'U': return new byte[] { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E }; - case 'V': return new byte[] { 0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04 }; - case 'W': return new byte[] { 0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A }; - case 'X': return new byte[] { 0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11 }; - case 'Y': return new byte[] { 0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04 }; - case 'Z': return new byte[] { 0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F }; - case '0': return new byte[] { 0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E }; - case '1': return new byte[] { 0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E }; - case '2': return new byte[] { 0x0E, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1F }; - case '3': return new byte[] { 0x1E, 0x01, 0x01, 0x0E, 0x01, 0x01, 0x1E }; - case '4': return new byte[] { 0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02 }; - case '5': return new byte[] { 0x1F, 0x10, 0x10, 0x1E, 0x01, 0x01, 0x1E }; - case '6': return new byte[] { 0x0E, 0x10, 0x10, 0x1E, 0x11, 0x11, 0x0E }; - case '7': return new byte[] { 0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08 }; - case '8': return new byte[] { 0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E }; - case '9': return new byte[] { 0x0E, 0x11, 0x11, 0x0F, 0x01, 0x01, 0x0E }; - case ':': return new byte[] { 0x00, 0x04, 0x04, 0x00, 0x04, 0x04, 0x00 }; - case '.': return new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04 }; - case '+': return new byte[] { 0x00, 0x04, 0x04, 0x1F, 0x04, 0x04, 0x00 }; - case '-': return new byte[] { 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00 }; - case '/': return new byte[] { 0x01, 0x02, 0x02, 0x04, 0x08, 0x08, 0x10 }; - case '%': return new byte[] { 0x19, 0x19, 0x02, 0x04, 0x08, 0x13, 0x13 }; - case ' ': return new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - default: return new byte[] { 0x1F, 0x11, 0x01, 0x02, 0x04, 0x00, 0x04 }; + byte[] glyph = glyphLookup[index]; + if (glyph != null) + { + return glyph; + } } + + return glyphLookup['?']; +} + +static byte[][] CreateGlyphLookup() +{ + byte[][] lookup = new byte[128][]; + + lookup['A'] = new byte[] { 0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11 }; + lookup['B'] = new byte[] { 0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E }; + lookup['C'] = new byte[] { 0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E }; + lookup['D'] = new byte[] { 0x1E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E }; + lookup['E'] = new byte[] { 0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F }; + lookup['F'] = new byte[] { 0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10 }; + lookup['G'] = new byte[] { 0x0E, 0x11, 0x10, 0x13, 0x11, 0x11, 0x0E }; + lookup['H'] = new byte[] { 0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11 }; + lookup['I'] = new byte[] { 0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E }; + lookup['J'] = new byte[] { 0x01, 0x01, 0x01, 0x01, 0x11, 0x11, 0x0E }; + lookup['K'] = new byte[] { 0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11 }; + lookup['L'] = new byte[] { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F }; + lookup['M'] = new byte[] { 0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11 }; + lookup['N'] = new byte[] { 0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x11 }; + lookup['O'] = new byte[] { 0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E }; + lookup['P'] = new byte[] { 0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10 }; + lookup['Q'] = new byte[] { 0x0E, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0D }; + lookup['R'] = new byte[] { 0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11 }; + lookup['S'] = new byte[] { 0x0F, 0x10, 0x10, 0x0E, 0x01, 0x01, 0x1E }; + lookup['T'] = new byte[] { 0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; + lookup['U'] = new byte[] { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E }; + lookup['V'] = new byte[] { 0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04 }; + lookup['W'] = new byte[] { 0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A }; + lookup['X'] = new byte[] { 0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11 }; + lookup['Y'] = new byte[] { 0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04 }; + lookup['Z'] = new byte[] { 0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F }; + lookup['0'] = new byte[] { 0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E }; + lookup['1'] = new byte[] { 0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E }; + lookup['2'] = new byte[] { 0x0E, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1F }; + lookup['3'] = new byte[] { 0x1E, 0x01, 0x01, 0x0E, 0x01, 0x01, 0x1E }; + lookup['4'] = new byte[] { 0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02 }; + lookup['5'] = new byte[] { 0x1F, 0x10, 0x10, 0x1E, 0x01, 0x01, 0x1E }; + lookup['6'] = new byte[] { 0x0E, 0x10, 0x10, 0x1E, 0x11, 0x11, 0x0E }; + lookup['7'] = new byte[] { 0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08 }; + lookup['8'] = new byte[] { 0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E }; + lookup['9'] = new byte[] { 0x0E, 0x11, 0x11, 0x0F, 0x01, 0x01, 0x0E }; + lookup[':'] = new byte[] { 0x00, 0x04, 0x04, 0x00, 0x04, 0x04, 0x00 }; + lookup['.'] = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04 }; + lookup['+'] = new byte[] { 0x00, 0x04, 0x04, 0x1F, 0x04, 0x04, 0x00 }; + lookup['-'] = new byte[] { 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00 }; + lookup['/'] = new byte[] { 0x01, 0x02, 0x02, 0x04, 0x08, 0x08, 0x10 }; + lookup['%'] = new byte[] { 0x19, 0x19, 0x02, 0x04, 0x08, 0x13, 0x13 }; + lookup[' '] = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + lookup['?'] = new byte[] { 0x1F, 0x11, 0x01, 0x02, 0x04, 0x00, 0x04 }; + + return lookup; } From bf3d3d95bc9245c3b931506f892c385a8e8ecdad Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 10 Jul 2026 17:37:36 +0200 Subject: [PATCH 8/9] more improvements following PR feedback --- Tests/M5StickS3TestApp/Program.cs | 2 +- nanoFramework.M5StickS3/M5StickS3.cs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Tests/M5StickS3TestApp/Program.cs b/Tests/M5StickS3TestApp/Program.cs index d6a77a2b..85905990 100644 --- a/Tests/M5StickS3TestApp/Program.cs +++ b/Tests/M5StickS3TestApp/Program.cs @@ -216,7 +216,7 @@ void DrawTelemetryLine(ushort y, string text, Color foreground, Color background } } - Screen.Write(0, y, width, (ushort)height, lineBuffer); + Screen.Write(0, y, (ushort)width, (ushort)height, lineBuffer); } byte[] GetGlyph(char c) diff --git a/nanoFramework.M5StickS3/M5StickS3.cs b/nanoFramework.M5StickS3/M5StickS3.cs index 87078bf4..af0d6ce6 100644 --- a/nanoFramework.M5StickS3/M5StickS3.cs +++ b/nanoFramework.M5StickS3/M5StickS3.cs @@ -192,12 +192,14 @@ public static void InitializeScreen(int memoryBitMapAllocation = Screen.DefaultM /// /// Gets a device on the internal I2C bus. /// - public static I2cDevice GetI2cDevice(int i2cDeviceAddress) => new I2cDevice(new I2cConnectionSettings(InternalI2cBusId, i2cDeviceAddress)); + public static I2cDevice GetI2cDevice(int i2cDeviceAddress) + => new I2cDevice(new I2cConnectionSettings(InternalI2cBusId, i2cDeviceAddress, I2cBusSpeed.StandardMode)); /// /// Gets a device on the Grove I2C bus. /// - public static I2cDevice GetGrove(int i2cDeviceAddress) => new I2cDevice(new I2cConnectionSettings(GroveI2cBusId, i2cDeviceAddress)); + public static I2cDevice GetGrove(int i2cDeviceAddress) + => new I2cDevice(new I2cConnectionSettings(GroveI2cBusId, i2cDeviceAddress, I2cBusSpeed.FastMode)); internal static void SetLcdPower(bool enabled) { From e89ccbc0ce943444660333dc0912160b72dcf23e Mon Sep 17 00:00:00 2001 From: Laurent Ellerbach Date: Fri, 10 Jul 2026 17:41:28 +0200 Subject: [PATCH 9/9] adding missing nuget --- nanoFramework.M5StickS3.nuspec | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nanoFramework.M5StickS3.nuspec b/nanoFramework.M5StickS3.nuspec index 8518f4e7..3e2576fc 100644 --- a/nanoFramework.M5StickS3.nuspec +++ b/nanoFramework.M5StickS3.nuspec @@ -23,8 +23,11 @@ + + +