Previously, we introduced the basics of .NET nanoFramework. In this article, we continue with the example of the Microweather ESP32-S2-Pico to discuss development with .NET nanoFramework: controlling the onboard RGB lights and external RGB lights. The content includes the significance of status lights, WS2812, HSV, PWM, and related knowledge.
1. Background
The first step in ESP32 development is basically to light up the lights, right? Lighting, lighting, lighting… I didn’t expect that this lighting would take me 2 weeks. One reason is that I was quite busy with work, and I only had spare time to work on this. Moreover, it also required searching for and learning various materials because the onboard lights are not that easy to control, and I made some mistakes along the way.
2. The Significance of Status Lights
I am not sure if there is a saying that the first step in ESP32 development is to light up the lights, but the role of the device’s status indicator light is indeed significant. It can provide users with information about the device’s working status. The device status light usually indicates different working states of the device using different colors or flashing frequencies. For example:
•Green: The device is working normally.•Red: The device has a fault.•Yellow: The device is performing an operation, such as upgrading.•Flashing: The device is performing an operation, such as data transmission.
The device status light is a very simple and understandable human-computer interaction method, allowing users to quickly understand the working status of the device and take timely action.
Typically, the device status light is located on the outside or front of the device, making it easy for users to view. For example, a router has a red-green light; the green light indicates that the network is normal, while the red light indicates a network issue. Similarly, in some simple small appliances, a red light indicates charging, while the light being off indicates that charging is complete.
3. Onboard LED
The onboard LED hardware of the ESP32-S2-Pico has undergone version modifications, and simply pulling up the GPIO does not light it up.
By checking the schematic, we can find that the LED is connected to GPIO9, and the circuit for the LED is WS2812B-0807. The schematic for this part is shown below, with only one LED:
4. WS2812
4.1 Introduction
WS2812 is a high-performance, low-cost full-color LED strip. It is a series-controlled light, allowing multiple LED lights to be connected together and controlled by serial signals to manage each LED’s color and brightness.
The main features of WS2812 include rich colors, high brightness, high dynamic range, and high precision in color temperature adjustment, enabling 256 levels of gray adjustment.
The control method of WS2812 is through a technique called single-wire control. This control method allows all LED lights to be controlled through a single wire, with each LED light having a built-in controller that can receive and decode the single-wire signal. This allows the controller to control the color and brightness of all LED lights by sending signals through a single wire.
WS2812 has a wide range of applications in many fields, such as LED light strips, LED dot matrix screens, LED tubes, LED rings, etc.
4.2 Control Principle
Each WS2812 LED light has a built-in controller that can receive and decode the single-wire signal. The controller distinguishes different data bits by the timing of high and low levels.
Usually, the color of each LED light consists of three data bits (red, green, blue), with each data bit representing 8 levels of brightness.
When the controller receives the single-wire signal, it first detects a high-level pulse, which is used to synchronize the data. Following this is 24 bits of data, consisting of 8 bits of red data, 8 bits of green data, and 8 bits of blue data. The controller decodes the signal according to this data format and sets the color and brightness of the LED light.
Since the focus of the article varies, here we only provide a simple introduction. Once you understand the principle, you can write your own driver, of course, you need to refer to the relevant hardware manual for a more detailed understanding of the data transmission methods, as the specific timing requirements for different LED communication may vary. Interested readers can look for information and try it themselves.
Principle introduction: https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf
Here we only need to use the already written driver, such as the neopixel library in MicroPython. Of course, .NET nanoFramework also has related libraries: Ws28xx.Esp32.
5. Onboard RGB Control
Here we use the ESP32 WS2812 Driver, code repository: https://github.com/nanoframework/nf-Community-Contributions/tree/master/drivers/ESP32-WS2812, which provides a PixelController
helper class to easily implement WS2812 control. You can also understand the implementation of the driver through the source code.
5.1 Simple Color Test
The constructor of PixelController
provides three parameters: GPIO, pixel count, and an optional parameter for RGBW light strip control, defaulting to false
.
PixelController controller = new PixelController(9, 1);// Simple color testcontroller.SetColor(0,255,0,0);// Redcontroller.UpdatePixels();Thread.Sleep(1000);controller.SetColor(0, 0, 255, 0);// Greenccontroller.UpdatePixels();Thread.Sleep(1000);controller.SetColor(0, 0, 0, 255);// Bluecontroller.UpdatePixels();Thread.Sleep(1000);controller.SetColor(0, 255, 255, 255);// Whitecontroller.UpdatePixels();Thread.Sleep(1000);
Effect:
5.2 HSV Color Model
The previous section simply tested the red, green, blue, and white colors of the light using SetColor
. Another way to set the color is to use SetHSVColor
, which utilizes the HSV color model.
HSV (Hue, Saturation, Value) and RGB (Red, Green, Blue) are two different color spaces. In the HSV color model:
•Hue (H) represents the type of color, such as red, yellow, blue, etc. The value of hue is measured in degrees, ranging from 0° to 360°, starting from red and calculated counterclockwise. Red is 0°, green is 120°, and blue is 240°. Their complementary colors are yellow at 60°, cyan at 180°, and purple at 300°;•Saturation (S) indicates how close the color is to the spectral color. The higher the value, the more saturated the color; the lower the value, the grayer the color. The saturation value ranges from 0 to 1.•Value (V) indicates the brightness of the color. The higher the value, the brighter the color; the lower the value, the darker the color. The value of V ranges from 0 to 1.
Compared to traditional RGB, HSV allows for smoother brightness changes by adjusting the brightness.
5.3 Breathing Light Effect
To achieve the breathing light effect, we simply need to use SetHSVColor
to set different brightness colors to smoothly implement the brightness change effect of the breathing light.
// Breathing light effectPixelController controller = new PixelController(9, 1);var ts = 0;for (; ; ){ var add = true; var v = 0f; for (; ; ) { controller.SetHSVColor(0, 240, 50, v); controller.UpdatePixels(); if (add) { v += 0.05f; } else { v -= 0.05f; } if (v >= 1) add = false; if (v <= 0) break; Thread.Sleep(50); } if (ts > 5) break; ts++;}
Effect:
5.4 Overall Color Test
For RGB colors, we can generate random colors by using three random numbers within 255. After understanding HSV, we can also directly sample colors evenly in a circular range based on the value of hue.
// Generate how many colors or the number of LED beadsuint ledCount = 25;PixelController controller = new PixelController(9, ledCount);// Add colors set at the beginningint step = (int)(360 / ledCount);var hue = 0;for (uint i = 0; i < ledCount; i++){ // HSV // Hue H ranges from 0° to 360° // Saturation S ranges from 0% to 100%, the higher the value, the more saturated the color // Value V indicates the brightness of the color, usually ranges from 0% (black) to 100% (white) // V is well set to 1, which is very bright controller.SetHSVColor((short)i, (short)hue, 1, 0.05f); hue = hue + step; controller.UpdatePixels();}// Loop to change colorfor (; ; ){ controller.MovePixelsByStep(1); controller.UpdatePixels(); Thread.Sleep(500);}
This code is more suitable for light strips, as the Microweather ESP32-S2-Pico has only one LED bead; using MovePixelsByStep
after changing the position with UpdatePixels
can also show color changes.
Many players enjoy RGB light effects, which can be considered “light pollution”. Using this to control light strips can be quite fun.
6. Official Driver Library Ws28xx.Esp32
Of course, it is best to use the official nanoFramework.Iot.Device.Ws28xx.Esp32
package, which can be installed via Nuget. It is important to choose the correct version, as the other nanoFramework
packages it depends on must correspond to the firmware you are flashing.
The usage is also simple; here is a simple example of RGB color control:
// 1 LED, 1 pixelconst int Count = 1;// LED Pin for Microweather ESP32-S2-Picoconst int Pin = 9;public static void Main(){ Ws28xx neo = new Ws2812c(Pin, Count); BitmapImage img = neo.Image; for (; ; ) { img.SetPixel(0, 0, Color.Red); neo.Update(); Thread.Sleep(500); img.SetPixel(0, 0, 0, 255, 0); neo.Update(); Thread.Sleep(500); img.SetPixel(0, 0, 0, 0, 255); neo.Update(); Thread.Sleep(500); }}
However, it is important to note that Ws28xx
needs to use Ws2812c
for instantiation; otherwise, the color control will not be correct.
7. Full-Color RGB
This section demonstrates controlling full-color red, green, and blue LED lights through PWM ports.
You can see that the module has 4 pins, with a voltage of 5V, connected to the VBUS pin; the other three pins are RBG control pins connected to GP2-4.
After connecting the device, the next step is to write code. First, we write the initialization function, defining R_Pin, B_Pin, and G_Pin, corresponding to GPIO that need to be set up for PWM. We use Configuration.SetPinFunction
to configure the corresponding pins. Then we create PWM channels from the three pins using PwmChannel.CreateFromPin
, and then start the control.
/// <summary>/// Define the PWM channels for RBG/// </summary>private static PwmChannel R_Pin, B_Pin, G_Pin;/// <summary>/// Initialize and configure PWM information/// </summary>public static void Init() { // Set the pins first, RBG connected to GP2-4 Configuration.SetPinFunction(2, DeviceFunction.PWM2); Configuration.SetPinFunction(3, DeviceFunction.PWM3); Configuration.SetPinFunction(4, DeviceFunction.PWM4); // Create PWM channels from the three pins R_Pin = PwmChannel.CreateFromPin(2, 40000, 0); B_Pin = PwmChannel.CreateFromPin(3, 40000, 0); G_Pin = PwmChannel.CreateFromPin(4, 40000, 0); // Start PWM R_Pin.Start(); B_Pin.Start(); G_Pin.Start();}
The core control code is also simple; here we randomly generate numbers within 255 and map them to percentages. The duty cycle of PwmChannel
is a percentage, of type double, ranging from 0 to 1.
public static void Main(){ Init(); Random random = new Random(); while (true) { R_Pin.DutyCycle = random.Next(255) / 255.0; B_Pin.DutyCycle = random.Next(255) / 255.0; G_Pin.DutyCycle = random.Next(255) / 255.0; Thread.Sleep(500); }}
8. Conclusion
All related codes in this article, as well as those from previous related series articles, have been open-sourced on GitHub. Interested readers can check it out: https://github.com/sangyuxiaowu/ESP32_S2_PICO.
If there are any errors or omissions, please feel free to point them out.