Environment setup: Refer to the Arduino IDE Getting Started Guide to install the IDE, then install the appropriate board package and driver library for your development board.
Libraries used:
Hardware used:


G2 (SDA) and G1 (SCL).0x25, corresponding to address dial position 0 on the Unit. If the dial is set to another position, change UNIT_I2C_ADDRESS in the program to the corresponding address.#include <M5Unified.h>
#include <M5Unit8Servos2.h>
constexpr int I2C_SDA_PIN = 2;
constexpr int I2C_SCL_PIN = 1;
constexpr uint8_t UNIT_I2C_ADDRESS = UNIT_8SERVOS2_DEFAULT_ADDR;
constexpr uint8_t SERVO_CHANNEL_COUNT = 8;
constexpr uint32_t I2C_FREQUENCY = 400000;
constexpr int16_t STATUS_LINE_HEIGHT = 40;
// Store the Unit controller and current servo position.
M5Unit8Servos2 servos2;
int16_t angle = 0;
int8_t angle_step = 20;
// Refresh the display with servo and power data.
void drawStatus(uint16_t dc_voltage, uint16_t grove_voltage, uint16_t current_mA)
{
M5.Display.fillScreen(TFT_BLACK);
M5.Display.setCursor(0, 0);
M5.Display.printf("Unit 8Servos2-I2C\n");
M5.Display.setCursor(0, STATUS_LINE_HEIGHT);
M5.Display.printf("Angle: %d deg", angle);
M5.Display.setCursor(0, STATUS_LINE_HEIGHT * 2);
M5.Display.printf("DC: %umV", static_cast<unsigned>(dc_voltage));
M5.Display.setCursor(0, STATUS_LINE_HEIGHT * 3);
M5.Display.printf("Current: %umA", static_cast<unsigned>(current_mA));
M5.Display.setCursor(0, STATUS_LINE_HEIGHT * 4);
M5.Display.printf("Grove: %umV", static_cast<unsigned>(grove_voltage));
}
void setup()
{
auto cfg = M5.config();
M5.begin(cfg);
Serial.begin(115200);
M5.Display.setFont(&fonts::FreeMonoBold12pt7b);
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK);
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN, I2C_FREQUENCY);
while (!servos2.begin(&Wire, UNIT_I2C_ADDRESS, -1, -1, I2C_FREQUENCY)) {
Serial.println("Unit 8Servos2-I2C not found");
M5.Display.fillScreen(TFT_BLACK);
M5.Display.setCursor(0, 0);
M5.Display.printf("Unit not found");
M5.Display.setCursor(0, STATUS_LINE_HEIGHT);
M5.Display.printf("Check I2C and address");
delay(1000);
}
// Set all channels to servo mode.
for (uint8_t channel = 0; channel < SERVO_CHANNEL_COUNT; ++channel) {
servos2.setMode(channel, M5_8SERVOS2_MODE_SERVO);
}
// Set both PWM timers to 50 Hz for standard servos.
servos2.setTimerFrequency(0, 50);
servos2.setTimerFrequency(1, 50);
// Read the initial power telemetry.
const uint16_t dc_voltage = servos2.getDCVoltage();
const uint16_t grove_voltage = servos2.getGroveVoltage();
const uint16_t current_mA = servos2.getSysCurrent();
Serial.println("Unit 8Servos2-I2C ready");
drawStatus(dc_voltage, grove_voltage, current_mA);
}
void loop()
{
// Apply the current angle to all servo channels.
for (uint8_t channel = 0; channel < SERVO_CHANNEL_COUNT; ++channel) {
servos2.setServoAngle(channel, static_cast<uint8_t>(angle));
}
// Read and report the latest power telemetry.
const uint16_t dc_voltage = servos2.getDCVoltage();
const uint16_t grove_voltage = servos2.getGroveVoltage();
const uint16_t current_mA = servos2.getSysCurrent();
Serial.printf(
"Servo angle: %d deg, DC: %u mV, Grove: %u mV, Current: %u mA\n", angle,
static_cast<unsigned>(dc_voltage), static_cast<unsigned>(grove_voltage),
static_cast<unsigned>(current_mA));
drawStatus(dc_voltage, grove_voltage, current_mA);
// Sweep the servos between 0 and 180 degrees.
angle += angle_step;
if (angle >= 180) {
angle = 180;
angle_step = -20;
} else if (angle <= 0) {
angle = 0;
angle_step = 20;
}
delay(200);
}
Example serial output:
Unit 8Servos2-I2C ready
Servo angle: 0 deg, DC: 6501 mV, Grove: 5068 mV, Current: 8 mA
Servo angle: 20 deg, DC: 5643 mV, Grove: 5070 mV, Current: 8 mA
Servo angle: 40 deg, DC: 5423 mV, Grove: 5066 mV, Current: 8 mA
Servo angle: 60 deg, DC: 5434 mV, Grove: 5072 mV, Current: 8 mA
Servo angle: 80 deg, DC: 5423 mV, Grove: 5074 mV, Current: 8 mA
Servo angle: 100 deg, DC: 5456 mV, Grove: 5072 mV, Current: 8 mA
Servo angle: 120 deg, DC: 5445 mV, Grove: 5074 mV, Current: 8 mA