Environment setup: Refer to the Arduino IDE Getting Started Tutorial to complete IDE installation, then install the corresponding board manager and required driver libraries according to the development board you are using.
Driver libraries used:
Hardware products used:


#include "M5Chain.h"
#include "M5Unified.h"
#define TXD_PIN GPIO_NUM_2
#define RXD_PIN GPIO_NUM_1
Chain M5Chain;
chain_status_t chain_status = CHAIN_OK;
uint8_t mono_id = 0;
uint8_t operation_status = 0;
bool findFirstMonoDevice()
{
// Enumerate devices and use the first Mono node
uint16_t device_nums = 0;
if (M5Chain.getDeviceNum(&device_nums) != CHAIN_OK || device_nums == 0) {
Serial.println("No Chain devices found");
return false;
}
device_info_t *infos = (device_info_t *)malloc(sizeof(device_info_t) * device_nums);
if (infos == nullptr) {
return false;
}
device_list_t devices;
devices.count = device_nums;
devices.devices = infos;
bool found = false;
if (M5Chain.getDeviceList(&devices)) {
for (uint8_t i = 0; i < devices.count; i++) {
if (devices.devices[i].device_type == CHAIN_MONO_TYPE_CODE) {
mono_id = devices.devices[i].id;
found = true;
break;
}
}
}
free(infos);
if (found) {
Serial.printf("Mono device found, id=%d\r\n", mono_id);
} else {
Serial.println("No Mono device found");
}
return found;
}
bool prepareMono()
{
// Initialize display mode, rotation, and brightness
if (!findFirstMonoDevice()) {
return false;
}
if (M5Chain.setMonoMode(mono_id, MONO_PIXEL_MODE, &operation_status) != CHAIN_OK || operation_status != 1) {
return false;
}
M5Chain.setMonoRotation(mono_id, MONO_ROTATION_0, &operation_status);
M5Chain.setMonoBrightness(mono_id, MONO_BRIGHTNESS_LEVEL_7, &operation_status);
M5Chain.setMonoClear(mono_id, &operation_status);
return true;
}
void setup()
{
M5.begin();
Serial.begin(115200);
Serial.println("Pixel demo");
M5Chain.begin(&Serial2, 115200, RXD_PIN, TXD_PIN);
if (!prepareMono()) {
Serial.println("Chain Mono init failed");
while (1) {
delay(100);
}
} else {
Serial.println("Chain Mono init success");
}
}
void loop()
{
static bool single_phase = true;
static uint8_t single_x = 0;
static uint8_t single_y = 0;
static uint8_t block_x = 0;
static uint8_t block_y = 4;
if (single_phase) {
// Walk through the first 4 rows with a single pixel
M5Chain.setMonoClear(mono_id, &operation_status);
chain_status = M5Chain.setMonoPixel(mono_id, single_x, single_y, true, &operation_status);
if (chain_status == CHAIN_OK && operation_status == 1) {
Serial.printf("Single pixel on: x=%d, y=%d\r\n", single_x, single_y);
}
delay(200);
single_x++;
if (single_x >= 8) {
single_x = 0;
single_y++;
}
if (single_y >= 4) {
single_phase = false;
block_x = 0;
block_y = 4;
}
} else {
// Walk through the last 4 rows with a 2x2 block
MonoPixelInfo pixels[] = {
{block_x, block_y, true},
{(uint8_t)(block_x + 1), block_y, true},
{block_x, (uint8_t)(block_y + 1), true},
{(uint8_t)(block_x + 1), (uint8_t)(block_y + 1), true}};
M5Chain.setMonoClear(mono_id, &operation_status);
chain_status = M5Chain.setMonoPixel(mono_id, pixels, sizeof(pixels) / sizeof(pixels[0]), &operation_status);
if (chain_status == CHAIN_OK && operation_status == 1) {
Serial.printf("Block on: (%d,%d) (%d,%d) (%d,%d) (%d,%d)\r\n", pixels[0].x, pixels[0].y, pixels[1].x,
pixels[1].y, pixels[2].x, pixels[2].y, pixels[3].x, pixels[3].y);
}
delay(200);
block_x++;
if (block_x >= 7) {
block_x = 0;
block_y += 2;
}
if (block_y >= 8) {
single_phase = true;
single_x = 0;
single_y = 0;
}
}
}The example effect is as follows:
#include "M5Chain.h"
#include "M5Unified.h"
#define TXD_PIN GPIO_NUM_2
#define RXD_PIN GPIO_NUM_1
Chain M5Chain;
uint8_t mono_id = 0;
uint8_t operation_status = 0;
const char text[] = "I'mChainMono";
bool findFirstMonoDevice()
{
// Enumerate devices and use the first Mono node
uint16_t device_nums = 0;
if (M5Chain.getDeviceNum(&device_nums) != CHAIN_OK || device_nums == 0) {
Serial.println("No Chain devices found");
return false;
}
device_info_t *infos = (device_info_t *)malloc(sizeof(device_info_t) * device_nums);
if (infos == nullptr) {
return false;
}
device_list_t devices;
devices.count = device_nums;
devices.devices = infos;
bool found = false;
if (M5Chain.getDeviceList(&devices)) {
for (uint8_t i = 0; i < devices.count; i++) {
if (devices.devices[i].device_type == CHAIN_MONO_TYPE_CODE) {
mono_id = devices.devices[i].id;
found = true;
break;
}
}
}
free(infos);
if (found) {
Serial.printf("Mono device found, id=%d\r\n", mono_id);
} else {
Serial.println("No Mono device found");
}
return found;
}
bool prepareMono()
{
// Initialize display mode, rotation, and brightness
if (!findFirstMonoDevice()) {
return false;
}
if (M5Chain.setMonoMode(mono_id, MONO_PIXEL_MODE, &operation_status) != CHAIN_OK || operation_status != 1) {
return false;
}
M5Chain.setMonoRotation(mono_id, MONO_ROTATION_0, &operation_status);
M5Chain.setMonoBrightness(mono_id, MONO_BRIGHTNESS_LEVEL_7, &operation_status);
M5Chain.setMonoClear(mono_id, &operation_status);
return true;
}
void setup()
{
M5.begin();
Serial.begin(115200);
Serial.println("Character display demo");
M5Chain.begin(&Serial2, 115200, RXD_PIN, TXD_PIN);
if (!prepareMono()) {
Serial.println("Chain Mono init failed");
while (1) {
delay(100);
}
} else {
Serial.println("Chain Mono init success");
}
}
void loop()
{
static uint8_t index = 0;
// Show characters one by one from the array
M5Chain.setMonoClear(mono_id, &operation_status);
M5Chain.setMonoPrintChar(mono_id, text[index], 1, 1, &operation_status);
Serial.printf("Character shown: %c\r\n", text[index]);
delay(400);
index++;
if (index >= sizeof(text) - 1) {
index = 0;
}
}The example effect is as follows:
#include "M5Chain.h"
#include "M5Unified.h"
#define TXD_PIN GPIO_NUM_2
#define RXD_PIN GPIO_NUM_1
Chain M5Chain;
uint8_t mono_id = 0;
uint8_t operation_status = 0;
// 8x8 arrow pattern
uint8_t arrow[8] = {
0b00011000, // ■■
0b00111100, // ■■■■
0b01111110, // ■■■■■■
0b11111111, // ■■■■■■■■
0b00111100, // ■■■■
0b00111100, // ■■■■
0b00111100, // ■■■■
0b00111100 // ■■■■
};
bool findFirstMonoDevice()
{
// Enumerate devices and use the first Mono node
uint16_t device_nums = 0;
if (M5Chain.getDeviceNum(&device_nums) != CHAIN_OK || device_nums == 0) {
Serial.println("No Chain devices found");
return false;
}
device_info_t *infos = (device_info_t *)malloc(sizeof(device_info_t) * device_nums);
if (infos == nullptr) {
return false;
}
device_list_t devices;
devices.count = device_nums;
devices.devices = infos;
bool found = false;
if (M5Chain.getDeviceList(&devices)) {
for (uint8_t i = 0; i < devices.count; i++) {
if (devices.devices[i].device_type == CHAIN_MONO_TYPE_CODE) {
mono_id = devices.devices[i].id;
found = true;
break;
}
}
}
free(infos);
if (found) {
Serial.printf("Mono device found, id=%d\r\n", mono_id);
} else {
Serial.println("No Mono device found");
}
return found;
}
bool prepareMono()
{
// Initialize pixel mode and brightness
if (!findFirstMonoDevice()) {
return false;
}
if (M5Chain.setMonoMode(mono_id, MONO_PIXEL_MODE, &operation_status) != CHAIN_OK || operation_status != 1) {
return false;
}
M5Chain.setMonoBrightness(mono_id, MONO_BRIGHTNESS_LEVEL_7, &operation_status);
M5Chain.setMonoClear(mono_id, &operation_status);
return true;
}
void setup()
{
M5.begin();
Serial.begin(115200);
Serial.println("Custom pattern rotation demo");
M5Chain.begin(&Serial2, 115200, RXD_PIN, TXD_PIN);
if (!prepareMono()) {
Serial.println("Chain Mono init failed");
while (1) {
delay(100);
}
} else {
Serial.println("Chain Mono init success");
}
}
void loop()
{
// Rotate through 0/90/180/270 degree display states
for (uint8_t rotation = MONO_ROTATION_0; rotation <= MONO_ROTATION_270; rotation++) {
M5Chain.setMonoRotation(mono_id, (mono_rotation_t)rotation, &operation_status);
M5Chain.setMonoBufferRefresh(mono_id, arrow, &operation_status);
Serial.printf("Rotation set to: %d°\r\n", rotation*90);
delay(800);
}
}The example effect is as follows:
#include "M5Chain.h"
#include "M5Unified.h"
#define TXD_PIN GPIO_NUM_2
#define RXD_PIN GPIO_NUM_1
Chain M5Chain;
chain_status_t chain_status = CHAIN_OK;
uint8_t mono_id = 0;
uint8_t operation_status = 0;
const char text[] = "Hi I'm Chain Mono! ";
bool findFirstMonoDevice()
{
// Enumerate devices and use the first Mono node
uint16_t device_nums = 0;
if (M5Chain.getDeviceNum(&device_nums) != CHAIN_OK || device_nums == 0) {
Serial.println("No Chain devices found");
return false;
}
device_info_t *infos = (device_info_t *)malloc(sizeof(device_info_t) * device_nums);
if (infos == nullptr) {
return false;
}
device_list_t devices;
devices.count = device_nums;
devices.devices = infos;
bool found = false;
if (M5Chain.getDeviceList(&devices)) {
for (uint8_t i = 0; i < devices.count; i++) {
if (devices.devices[i].device_type == CHAIN_MONO_TYPE_CODE) {
mono_id = devices.devices[i].id;
found = true;
break;
}
}
}
free(infos);
if (found) {
Serial.printf("Mono device found, id=%d\r\n", mono_id);
} else {
Serial.println("No Mono device found");
}
return found;
}
bool prepareMono()
{
// Switch to string scroll mode and set basic parameters
if (!findFirstMonoDevice()) {
return false;
}
if (M5Chain.setMonoMode(mono_id, MONO_STRING_SCROLL_MODE, &operation_status) != CHAIN_OK ||
operation_status != 1) {
return false;
}
M5Chain.setMonoRotation(mono_id, MONO_ROTATION_0, &operation_status);
M5Chain.setMonoBrightness(mono_id, MONO_BRIGHTNESS_LEVEL_7, &operation_status);
return true;
}
void setup()
{
M5.begin();
Serial.begin(115200);
Serial.println("Mono string scroll demo");
M5Chain.begin(&Serial2, 115200, RXD_PIN, TXD_PIN);
if (!prepareMono()) {
Serial.println("Chain Mono init failed");
while (1) {
delay(100);
}
} else {
Serial.println("Chain Mono init success");
}
chain_status = M5Chain.setMonoStringScroll(mono_id, text, MONO_SCROLL_LEFT, MONO_SCROLL_MODE_LOOP, 100,
&operation_status);
if (chain_status == CHAIN_OK && operation_status == 1) {
Serial.printf("String scroll started: %s\r\n", text);
}
}
void loop()
{
delay(1000);
}The example effect is as follows:
