English
English
简体中文
日本語

Arduino Quick Start

2. Devices & Examples

5. Extensions

6. Applications

Unit 8Servos2-I2C Arduino Tutorial

1. Preparation

2. Notes

Pin Compatibility
Since the pin configuration differs between host devices, M5Stack provides a pin compatibility table for reference. Check the actual pin connections and modify the example program as needed.

3. Example Program

  • This tutorial uses a CoreS3 with Unit 8Servos2-I2C to control servos. Unit 8Servos2-I2C communicates over I2C. When connected, the corresponding pins are G2 (SDA) and G1 (SCL).
I2C Address
The default I2C address of Unit 8Servos2-I2C is 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.
cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
#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);
}

4. Compile and Upload

  • Copy the example program above into the project code area, select the device port (see Program Compilation and Upload for details), then click the compile and upload button in the upper-left corner of Arduino IDE. Wait for the program to finish compiling and uploading to the device.

5. Servo Control

  • After startup, the program sets all 8 channels to servo mode and controls the servos at 50 Hz. The servos move synchronously between 0° and 180° in 20° steps. The display shows the current angle, DC input voltage, system current, and Grove port voltage, while the serial output reports the same data.

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
Page Tools
PDF
On This Page