

G2 (SDA) and G1 (SCL) on the CoreS3.M5UnitEncoderMotor class with the product selector M5Unit_EncoderMotor_Driver (default address 0x2F, one channel). Include #include <M5Unit-EncoderMotor-Driver.h>; the display functionality also requires the M5Unified and M5GFX libraries listed above.#include <M5Unified.h>
#include <M5Unit-EncoderMotor-Driver.h>
// Product selector sets the default address and channel count:
// U232 = 0x2F (1 channel), U233 = 0x24 (4 channels)
M5UnitEncoderMotor motor(M5Unit_EncoderMotor_Driver);
// Draw into an offscreen canvas so a full screen refresh does not flicker
M5Canvas canvas(&M5.Display);
constexpr int8_t I2C_SDA_PIN = 2; // CoreS3 PORT.A
constexpr int8_t I2C_SCL_PIN = 1;
constexpr uint32_t I2C_FREQUENCY = 400000;
constexpr int8_t PWM_DUTY = 100; // -127 ~ 127
constexpr uint32_t RUN_MS = 3000; // Duration of each segment
constexpr uint32_t STEP_MS = 200; // Sampling interval
void halt(const char *message)
{
Serial.println(message);
while (true) {
delay(1000);
}
}
// Center the status screen and mix three font sizes so the key value stands out.
void showScreen(const char *title, const char *value, const char *detail1, const char *detail2)
{
canvas.fillScreen(TFT_BLACK);
canvas.setTextDatum(middle_center);
canvas.setFont(&fonts::DejaVu18);
canvas.setTextColor(TFT_LIGHTGREY);
canvas.drawString(title, canvas.width() / 2, 32);
canvas.setFont(&fonts::DejaVu40);
canvas.setTextColor(TFT_WHITE);
canvas.drawString(value, canvas.width() / 2, 112);
canvas.setFont(&fonts::DejaVu12);
canvas.setTextColor(TFT_LIGHTGREY);
if (detail1 && detail1[0]) {
canvas.drawString(detail1, canvas.width() / 2, 176);
}
if (detail2 && detail2[0]) {
canvas.drawString(detail2, canvas.width() / 2, 204);
}
canvas.pushSprite(0, 0);
}
void drawStatus(const char *state, int8_t duty)
{
int8_t speed = 0;
int32_t encoder = 0;
motor.getSpeed(speed);
motor.getEncoder(encoder);
char value[16];
char detail1[48];
char detail2[48];
snprintf(value, sizeof(value), "%+d", speed);
snprintf(detail1, sizeof(detail1), "%s duty %+d", state, duty);
snprintf(detail2, sizeof(detail2), "encoder %ld", static_cast<long>(encoder));
showScreen("PWM", value, detail1, detail2);
}
// Apply the duty cycle and keep reporting speed and encoder counts
void holdDuty(const char *state, int8_t duty, uint32_t duration_ms)
{
if (!motor.setPWMDuty(duty)) {
halt("Failed to set PWM duty.");
}
for (uint32_t elapsed = 0; elapsed < duration_ms; elapsed += STEP_MS) {
int8_t speed = 0;
int32_t encoder = 0;
motor.getSpeed(speed);
motor.getEncoder(encoder);
drawStatus(state, duty);
Serial.printf("%s duty=%+d speed=%+d encoder=%ld\n", state, duty, speed,
static_cast<long>(encoder));
delay(STEP_MS);
}
}
void setup()
{
auto cfg = M5.config();
cfg.output_power = true;
M5.begin(cfg);
canvas.createSprite(M5.Display.width(), M5.Display.height());
Serial.begin(115200);
// Address 0 selects the default address of the selected product
while (!motor.begin(&Wire, 0, I2C_SDA_PIN, I2C_SCL_PIN, I2C_FREQUENCY)) {
Serial.println("Unit EncoderMotor Driver not found");
delay(1000);
}
delay(100); // give the unit time to finish the previous write
// Soft start is saved in the unit flash; turn it off so this demo runs without a ramp
if (!motor.setSoftStartStop(false)) {
halt("Failed to disable soft start / stop.");
}
if (!motor.setMotorRun(true) || !motor.setMotorMode(M5_UNIT_ENCODER_MOTOR_MODE_NORMAL) || !motor.setPWMDuty(0)) {
halt("Failed to initialise PWM mode.");
}
Serial.println("Unit EncoderMotor Driver ready");
}
void loop()
{
holdDuty("forward", PWM_DUTY, RUN_MS);
holdDuty("coast", 0, 1000);
holdDuty("reverse", -PWM_DUTY, RUN_MS);
holdDuty("coast", 0, 1000);
}The program constructs a M5UnitEncoderMotor object with M5Unit_EncoderMotor_Driver. Passing address 0 to begin() selects the default address 0x2F. It then enables the driver with setMotorRun(true), enters PWM open-loop mode with setMotorMode(M5_UNIT_ENCODER_MOTOR_MODE_NORMAL), and clears the duty cycle with setPWMDuty(0). The loop runs forward at duty 100 for 3 seconds, coasts at zero duty for 1 second, and runs in reverse at -100 for 3 seconds, repeating this sequence. In PWM mode, the duty cycle acts directly on the H-bridge without closed-loop speed regulation; the relationship between duty and speed varies with supply voltage and load. Every 200 ms, getSpeed() and getEncoder() provide the measured speed (encoder counts per 20 ms) and encoder count for serial output.
Example serial output:
Unit EncoderMotor Driver ready
forward duty=+100 speed=+0 encoder=0
forward duty=+100 speed=+8 encoder=92
forward duty=+100 speed=+12 encoder=221
forward duty=+100 speed=+17 encoder=404
forward duty=+100 speed=+17 encoder=1008
forward duty=+100 speed=+16 encoder=2226
coast duty=+0 speed=+15 encoder=2622
coast duty=+0 speed=+7 encoder=2875
coast duty=+0 speed=+0 encoder=3021
reverse duty=-100 speed=+0 encoder=3021
reverse duty=-100 speed=-6 encoder=2960
reverse duty=-100 speed=-16 encoder=2712 #include <M5Unified.h>
#include <M5Unit-EncoderMotor-Driver.h>
// Product selector sets the default address and channel count:
// U232 = 0x2F (1 channel), U233 = 0x24 (4 channels)
M5UnitEncoderMotor motor(M5Unit_EncoderMotor_Driver);
// Draw into an offscreen canvas so a full screen refresh does not flicker
M5Canvas canvas(&M5.Display);
constexpr int8_t I2C_SDA_PIN = 2; // CoreS3 PORT.A
constexpr int8_t I2C_SCL_PIN = 1;
constexpr uint32_t I2C_FREQUENCY = 400000;
constexpr int8_t TARGET_SPEED = 15; // Encoder counts per 20 ms
constexpr M5UnitEncoderMotorPID SPEED_PID = {10, 1, 15}; // Default speed loop P / I / D
constexpr uint32_t RUN_MS = 5000;
constexpr uint32_t STEP_MS = 200;
void halt(const char *message)
{
Serial.println(message);
while (true) {
delay(1000);
}
}
// Center the status screen and mix three font sizes so the key value stands out.
void showScreen(const char *title, const char *value, const char *detail1, const char *detail2)
{
canvas.fillScreen(TFT_BLACK);
canvas.setTextDatum(middle_center);
canvas.setFont(&fonts::DejaVu18);
canvas.setTextColor(TFT_LIGHTGREY);
canvas.drawString(title, canvas.width() / 2, 32);
canvas.setFont(&fonts::DejaVu40);
canvas.setTextColor(TFT_WHITE);
canvas.drawString(value, canvas.width() / 2, 112);
canvas.setFont(&fonts::DejaVu12);
canvas.setTextColor(TFT_LIGHTGREY);
if (detail1 && detail1[0]) {
canvas.drawString(detail1, canvas.width() / 2, 176);
}
if (detail2 && detail2[0]) {
canvas.drawString(detail2, canvas.width() / 2, 204);
}
canvas.pushSprite(0, 0);
}
void drawStatus(const char *state, int8_t target)
{
int8_t speed = 0;
int8_t duty = 0;
motor.getSpeed(speed);
motor.getPWMDuty(duty);
char value[16];
char detail1[48];
char detail2[48];
snprintf(value, sizeof(value), "%+d", speed);
snprintf(detail1, sizeof(detail1), "%s target %+d", state, target);
snprintf(detail2, sizeof(detail2), "duty %+d", duty);
showScreen("SPEED", value, detail1, detail2);
}
// Hold a target speed and print the closed loop response
void holdSpeed(const char *state, int8_t target, uint32_t duration_ms)
{
if (!motor.runSpeed(target, SPEED_PID)) {
halt("Failed to run in Speed mode.");
}
for (uint32_t elapsed = 0; elapsed < duration_ms; elapsed += STEP_MS) {
int8_t speed = 0;
int8_t duty = 0;
motor.getSpeed(speed);
motor.getPWMDuty(duty);
drawStatus(state, target);
Serial.printf("%s target=%+d measured=%+d duty=%+d\n", state, target, speed, duty);
delay(STEP_MS);
}
}
void setup()
{
auto cfg = M5.config();
cfg.output_power = true;
M5.begin(cfg);
canvas.createSprite(M5.Display.width(), M5.Display.height());
Serial.begin(115200);
while (!motor.begin(&Wire, 0, I2C_SDA_PIN, I2C_SCL_PIN, I2C_FREQUENCY)) {
Serial.println("Unit EncoderMotor Driver not found");
delay(1000);
}
delay(100); // give the unit time to finish the previous write
// Soft start is saved in the unit flash; turn it off so this demo runs without a ramp
if (!motor.setSoftStartStop(false)) {
halt("Failed to disable soft start / stop.");
}
if (!motor.setMotorRun(true) || !motor.setMotorMode(M5_UNIT_ENCODER_MOTOR_MODE_NORMAL) || !motor.setPWMDuty(0)) {
halt("Failed to initialise Speed mode.");
}
Serial.println("Unit EncoderMotor Driver ready");
}
void loop()
{
holdSpeed("run", TARGET_SPEED, RUN_MS);
holdSpeed("stop", 0, 2000);
}The program calls runSpeed(15, SPEED_PID) with the default speed-loop parameters {10, 1, 15} to enter closed-loop Speed mode. The target is 15, measured in encoder-count increments per 20 ms, not RPM. The range is -127 ~ 127, with the sign indicating direction. After 5 seconds, the target changes to 0 to observe deceleration. The loop prints the target, measured speed, and current PWM duty every 200 ms.
Example serial output:
Unit EncoderMotor Driver ready
run target=+15 measured=+15 duty=+0
run target=+15 measured=+15 duty=+107
run target=+15 measured=+15 duty=+80
run target=+15 measured=+14 duty=+92
run target=+15 measured=+15 duty=+91
run target=+15 measured=+15 duty=+89
run target=+15 measured=+15 duty=+88
stop target=+0 measured=+14 duty=+0
stop target=+0 measured=+6 duty=+0
stop target=+0 measured=+0 duty=+0 #include <M5Unified.h>
#include <M5Unit-EncoderMotor-Driver.h>
// Product selector sets the default address and channel count:
// U232 = 0x2F (1 channel), U233 = 0x24 (4 channels)
M5UnitEncoderMotor motor(M5Unit_EncoderMotor_Driver);
// Draw into an offscreen canvas so a full screen refresh does not flicker
M5Canvas canvas(&M5.Display);
constexpr int8_t I2C_SDA_PIN = 2; // CoreS3 PORT.A
constexpr int8_t I2C_SCL_PIN = 1;
constexpr uint32_t I2C_FREQUENCY = 400000;
constexpr int32_t TARGET_POSITION = 1320; // One output revolution: 11 PPR x 4 edges x 30 gear ratio = 1320 counts
constexpr int8_t MAX_SPEED = 60; // Position loop output limit, the firmware uses the absolute value
constexpr M5UnitEncoderMotorPID POSITION_PID = {1, 1, 17}; // Default position loop P / I / D
constexpr uint32_t TIMEOUT_MS = 8000;
constexpr uint32_t STEP_MS = 200;
void halt(const char *message)
{
Serial.println(message);
while (true) {
delay(1000);
}
}
// Center the status screen and mix three font sizes so the key value stands out.
void showScreen(const char *title, const char *value, const char *detail1, const char *detail2)
{
canvas.fillScreen(TFT_BLACK);
canvas.setTextDatum(middle_center);
canvas.setFont(&fonts::DejaVu18);
canvas.setTextColor(TFT_LIGHTGREY);
canvas.drawString(title, canvas.width() / 2, 32);
canvas.setFont(&fonts::DejaVu40);
canvas.setTextColor(TFT_WHITE);
canvas.drawString(value, canvas.width() / 2, 112);
canvas.setFont(&fonts::DejaVu12);
canvas.setTextColor(TFT_LIGHTGREY);
if (detail1 && detail1[0]) {
canvas.drawString(detail1, canvas.width() / 2, 176);
}
if (detail2 && detail2[0]) {
canvas.drawString(detail2, canvas.width() / 2, 204);
}
canvas.pushSprite(0, 0);
}
void drawStatus(int32_t target)
{
int32_t encoder = 0;
int8_t speed = 0;
motor.getEncoder(encoder);
motor.getSpeed(speed);
char value[16];
char detail1[48];
char detail2[48];
snprintf(value, sizeof(value), "%ld", static_cast<long>(encoder));
snprintf(detail1, sizeof(detail1), "target %ld", static_cast<long>(target));
snprintf(detail2, sizeof(detail2), "speed %+d", speed);
showScreen("POSITION", value, detail1, detail2);
}
// Send the target position and wait until it is reached
void moveTo(int32_t target, uint32_t timeout_ms)
{
if (!motor.runPosition(target, MAX_SPEED, POSITION_PID)) {
halt("Failed to run in Position mode.");
}
for (uint32_t elapsed = 0; elapsed < timeout_ms; elapsed += STEP_MS) {
int32_t encoder = 0;
motor.getEncoder(encoder);
drawStatus(target);
Serial.printf("target=%ld encoder=%ld\n", static_cast<long>(target), static_cast<long>(encoder));
if (abs(target - encoder) <= 10) {
Serial.println("position reached");
return;
}
delay(STEP_MS);
}
Serial.println("position timeout");
}
void setup()
{
auto cfg = M5.config();
cfg.output_power = true;
M5.begin(cfg);
canvas.createSprite(M5.Display.width(), M5.Display.height());
Serial.begin(115200);
while (!motor.begin(&Wire, 0, I2C_SDA_PIN, I2C_SCL_PIN, I2C_FREQUENCY)) {
Serial.println("Unit EncoderMotor Driver not found");
delay(1000);
}
delay(100); // give the unit time to finish the previous write
// Soft start is saved in the unit flash; turn it off so this demo runs without a ramp
if (!motor.setSoftStartStop(false)) {
halt("Failed to disable soft start / stop.");
}
if (!motor.setMotorRun(true) || !motor.setMotorMode(M5_UNIT_ENCODER_MOTOR_MODE_NORMAL) || !motor.setPWMDuty(0) ||
!motor.clearEncoder()) {
halt("Failed to initialise Position mode.");
}
Serial.println("Unit EncoderMotor Driver ready");
}
void loop()
{
moveTo(TARGET_POSITION, TIMEOUT_MS);
delay(1000);
moveTo(0, TIMEOUT_MS);
delay(1000);
}The program first clears the encoder count with clearEncoder(), then calls runPosition(1320, 60, POSITION_PID) to enter closed-loop Position mode. The target 1320 corresponds to one output-shaft revolution (11 PPR × 4-edge decoding × 30 gear ratio = 1320 counts). The value 60 limits the position-loop output; the firmware uses its absolute value as the maximum speed. POSITION_PID = {1, 1, 17} contains the firmware’s default position-loop parameters. During movement, the target and current encoder count are printed every 200 ms. Once the target is reached, the program prints position reached and then returns to position 0.
Example serial output:
Unit EncoderMotor Driver ready
target=1320 encoder=0
target=1320 encoder=184
target=1320 encoder=536
target=1320 encoder=884
target=1320 encoder=1286
target=1320 encoder=1323
position reached
target=0 encoder=1323
target=0 encoder=1146
target=0 encoder=520
target=0 encoder=4
position reached #include <M5Unified.h>
#include <M5Unit-EncoderMotor-Driver.h>
// Product selector sets the default address and channel count:
// U232 = 0x2F (1 channel), U233 = 0x24 (4 channels)
M5UnitEncoderMotor motor(M5Unit_EncoderMotor_Driver);
// Draw into an offscreen canvas so a full screen refresh does not flicker
M5Canvas canvas(&M5.Display);
constexpr int8_t I2C_SDA_PIN = 2; // CoreS3 PORT.A
constexpr int8_t I2C_SCL_PIN = 1;
constexpr uint32_t I2C_FREQUENCY = 400000;
constexpr int8_t TARGET_SPEED = 20; // Encoder counts per 20 ms
constexpr M5UnitEncoderMotorPID SPEED_PID = {10, 1, 15}; // Default speed loop P / I / D
constexpr uint32_t RUN_MS = 4000;
constexpr uint32_t STEP_MS = 50;
void halt(const char *message)
{
Serial.println(message);
while (true) {
delay(1000);
}
}
// Center the status screen and mix three font sizes so the key value stands out.
void showScreen(const char *title, const char *value, const char *detail1, const char *detail2)
{
canvas.fillScreen(TFT_BLACK);
canvas.setTextDatum(middle_center);
canvas.setFont(&fonts::DejaVu18);
canvas.setTextColor(TFT_LIGHTGREY);
canvas.drawString(title, canvas.width() / 2, 32);
canvas.setFont(&fonts::DejaVu40);
canvas.setTextColor(TFT_WHITE);
canvas.drawString(value, canvas.width() / 2, 112);
canvas.setFont(&fonts::DejaVu12);
canvas.setTextColor(TFT_LIGHTGREY);
if (detail1 && detail1[0]) {
canvas.drawString(detail1, canvas.width() / 2, 176);
}
if (detail2 && detail2[0]) {
canvas.drawString(detail2, canvas.width() / 2, 204);
}
canvas.pushSprite(0, 0);
}
void drawStatus(const char *state, int8_t target, int8_t speed)
{
char value[16];
char detail1[48];
char detail2[48];
snprintf(value, sizeof(value), "%+d", speed);
snprintf(detail1, sizeof(detail1), "%s target %+d", state, target);
snprintf(detail2, sizeof(detail2), "soft start on");
showScreen("SOFT START", value, detail1, detail2);
}
// Drive the target speed and sample it to show the ramp.
void holdSpeed(const char *state, int8_t target, uint32_t duration_ms)
{
motor.runSpeed(target, SPEED_PID);
for (uint32_t elapsed = 0; elapsed < duration_ms; elapsed += STEP_MS) {
int8_t speed = 0;
int32_t encoder = 0;
motor.getSpeed(speed);
motor.getEncoder(encoder);
drawStatus(state, target, speed);
Serial.printf("%s t=%4lums target=%+d measured=%+d encoder=%ld\n", state, static_cast<long>(elapsed),
target, speed, static_cast<long>(encoder));
delay(STEP_MS);
}
}
void setup()
{
auto cfg = M5.config();
cfg.output_power = true;
M5.begin(cfg);
canvas.createSprite(M5.Display.width(), M5.Display.height());
Serial.begin(115200);
while (!motor.begin(&Wire, 0, I2C_SDA_PIN, I2C_SCL_PIN, I2C_FREQUENCY)) {
Serial.println("Unit EncoderMotor Driver not found");
delay(1000);
}
delay(100); // give the unit time to finish the previous write
if (!motor.setMotorRun(true) || !motor.setMotorMode(M5_UNIT_ENCODER_MOTOR_MODE_SPEED) || !motor.setPWMDuty(0)) {
halt("Failed to initialise speed mode.");
}
// The soft start / stop bit is saved in the unit flash, so set it once in setup()
if (!motor.setSoftStartStop(true)) {
halt("Failed to enable soft start / stop.");
}
Serial.println("Soft start / stop enabled");
Serial.println("Unit EncoderMotor Driver ready");
}
void loop()
{
holdSpeed("run", TARGET_SPEED, RUN_MS);
holdSpeed("stop", 0, RUN_MS);
}Soft start/stop is implemented by the unit firmware (register 0xD1). After setSoftStartStop(true) is enabled, changes to the speed command follow an internal ramp rather than a direct step. This setting is saved to the unit flash and retained after power loss, so it only needs to be set once in setup().
This example uses Speed mode: runSpeed(20, SPEED_PID) starts the motor, and runSpeed(0, SPEED_PID) stops it after 4 seconds. The measured speed is sampled every 50 ms. The speed loop quickly drives the duty cycle to its maximum, so speed rises faster than in PWM mode. To observe a more noticeable duty-cycle ramp, use PWM mode instead (see 3.1).
When powered through the 5 V Grove interface, applying a speed-loop step command from rest can cause a large instantaneous current. An external DC power supply is recommended.
Example serial output:
Soft start / stop enabled
Unit EncoderMotor Driver ready
run t= 0ms target=+20 measured=+0
run t= 50ms target=+20 measured=+4
run t= 100ms target=+20 measured=+13
run t= 150ms target=+20 measured=+19
run t= 200ms target=+20 measured=+21
run t= 400ms target=+20 measured=+20
run t=1000ms target=+20 measured=+21
stop t= 0ms target=+0 measured=+20
stop t= 50ms target=+0 measured=+7
stop t= 100ms target=+0 measured=+0 #include <M5Unified.h>
#include <M5Unit-EncoderMotor-Driver.h>
// Product selector sets the default address and channel count:
// U232 = 0x2F (1 channel), U233 = 0x24 (4 channels)
M5UnitEncoderMotor motor(M5Unit_EncoderMotor_Driver);
// Draw into an offscreen canvas so a full screen refresh does not flicker
M5Canvas canvas(&M5.Display);
constexpr int8_t I2C_SDA_PIN = 2; // CoreS3 PORT.A
constexpr int8_t I2C_SCL_PIN = 1;
constexpr uint32_t I2C_FREQUENCY = 400000;
struct ColorStep {
const char *name;
uint8_t red;
uint8_t green;
uint8_t blue;
};
constexpr ColorStep STEPS[] = {
{"red", 255, 0, 0}, {"green", 0, 255, 0}, {"blue", 0, 0, 255}, {"yellow", 255, 255, 0},
{"cyan", 0, 255, 255}, {"magenta", 255, 0, 255}, {"off", 0, 0, 0},
};
constexpr uint8_t BRIGHTNESS = 80; // 0 ~ 255
constexpr uint32_t STEP_MS = 800;
void halt(const char *message)
{
Serial.println(message);
while (true) {
delay(1000);
}
}
// Center the status screen and mix three font sizes so the key value stands out.
void showScreen(const char *title, const char *value, const char *detail1, const char *detail2)
{
canvas.fillScreen(TFT_BLACK);
canvas.setTextDatum(middle_center);
canvas.setFont(&fonts::DejaVu18);
canvas.setTextColor(TFT_LIGHTGREY);
canvas.drawString(title, canvas.width() / 2, 32);
canvas.setFont(&fonts::DejaVu40);
canvas.setTextColor(TFT_WHITE);
canvas.drawString(value, canvas.width() / 2, 112);
canvas.setFont(&fonts::DejaVu12);
canvas.setTextColor(TFT_LIGHTGREY);
if (detail1 && detail1[0]) {
canvas.drawString(detail1, canvas.width() / 2, 176);
}
if (detail2 && detail2[0]) {
canvas.drawString(detail2, canvas.width() / 2, 204);
}
canvas.pushSprite(0, 0);
}
void setup()
{
auto cfg = M5.config();
cfg.output_power = true;
M5.begin(cfg);
canvas.createSprite(M5.Display.width(), M5.Display.height());
Serial.begin(115200);
while (!motor.begin(&Wire, 0, I2C_SDA_PIN, I2C_SCL_PIN, I2C_FREQUENCY)) {
Serial.println("Unit EncoderMotor Driver not found");
delay(1000);
}
if (!motor.setRGBBrightness(BRIGHTNESS)) {
halt("Failed to set RGB brightness.");
}
Serial.println("RGB demo ready");
}
void loop()
{
for (const auto &step : STEPS) {
const bool ok = motor.setRGBColor(step.red, step.green, step.blue);
delay(100);
uint8_t red = 0;
uint8_t green = 0;
uint8_t blue = 0;
uint8_t brightness = 0;
const bool read_ok = motor.getRGBColor(red, green, blue) && motor.getRGBBrightness(brightness);
char value[16];
char detail1[48];
char detail2[48];
snprintf(value, sizeof(value), "%s", step.name);
snprintf(detail1, sizeof(detail1), "R%3u G%3u B%3u", red, green, blue);
snprintf(detail2, sizeof(detail2), "brightness %u", brightness);
showScreen("RGB", value, detail1, detail2);
Serial.printf("set=%s rgb=(%u,%u,%u) brightness=%u result=%s read=(%u,%u,%u)/%u read_result=%s\n",
step.name, step.red, step.green, step.blue, BRIGHTNESS, ok ? "OK" : "FAIL", red, green,
blue, brightness, read_ok ? "OK" : "FAIL");
delay(STEP_MS);
}
}setRGBBrightness(80) sets the brightness (0 ~ 255). The program then sends red, green, blue, yellow, cyan, magenta, and off in sequence. Each step reads back the color and brightness using getRGBColor() and getRGBBrightness(). The display shows the color name and read-back values; the serial output also prints the requested values and the read/write results.
Example serial output:
RGB demo ready
set=red rgb=(255,0,0) brightness=80 result=OK read=(255,0,0)/80 read_result=OK
set=green rgb=(0,255,0) brightness=80 result=OK read=(0,255,0)/80 read_result=OK
set=blue rgb=(0,0,255) brightness=80 result=OK read=(0,0,255)/80 read_result=OK
set=yellow rgb=(255,255,0) brightness=80 result=OK read=(255,255,0)/80 read_result=OK
set=cyan rgb=(0,255,255) brightness=80 result=OK read=(0,255,255)/80 read_result=OK
set=magenta rgb=(255,0,255) brightness=80 result=OK read=(255,0,255)/80 read_result=OK
set=off rgb=(0,0,0) brightness=80 result=OK read=(0,0,0)/80 read_result=OK Copy one of the example programs above into the sketch editor and select the device port (see Compile and Upload). Click the Upload button in the upper-left corner of the Arduino IDE and wait for compilation and upload to finish.