M5Atom and Grove Sensors

  • I'm trying to use M5Atom with any Grove sensor, and aren't making any progress.

    An I2C scan finds one address in use (0x68). This is returned even when no sensors are plugged into the Grove port, and plugging in a sensor does not return any additional addresses.

    1. What exactly is the device on 0x68?
    2. Can the Grove port be used with I2C sensors? If so, how?

    Thanks in advance for any help!

  • @madgeometer

    1. The device on 0x68 is MPU6886 (gyro, accelerometer).
      I have checked the sample program for the MPU6886 (it worked):
    2. The Atom grove port is for I2C devices (similar to other M5 cores, for example, M5Stack Fire is Port A). So you can connect any I2C Unit to this port. There is no Arduino example available for connecting an I2C M5 Unit to an Atom but the main difference is only in the GPIO Pin definition. The Atom I2C uses GPIO Pin 26 and 32. These are different with for example M5Stack Fire Port A use GPIO Pin 21 and 22.
      I do not have time to try connecting a Unit to an Atom, but I will share my test in the future.

  • @madgeometer I have just tried to connect a TOF Unit to my Atom Matrix via its grove port. Since the Atom Matrix does not have LCD than the distance measurement results are sent via the serial port. The code is taken from the TOF Unit example for M5 Stack. I modified:

    1. Wire.begin() -> Wire.begin(26,32)
    2. M5.begin() is moved BEFORE the Wire.begin(26,32)

    Check this list:

    //  the original code by Ted Meyers
    //  posted here: https://groups.google.com/d/msg/diyrovers/lc7NUZYuJOg/ICPrYNJGBgAJ
    //  if your tof have some problem, please see https://docs.m5stack.com/#/en/unit/tof
    //  Modified by from M5Stack Fire Example to be suited for M5 Atom Matrix (liemph, 2020)
    #include "M5Atom.h"
    #include <Wire.h>
    #define VL53L0X_REG_IDENTIFICATION_MODEL_ID         0xc0
    #define VL53L0X_REG_SYSRANGE_START                  0x00
    #define VL53L0X_REG_RESULT_INTERRUPT_STATUS         0x13
    #define VL53L0X_REG_RESULT_RANGE_STATUS             0x14
    #define address 0x29
    byte gbuf[16];
    void setup() {
      // put your setup code here, to run once:
      M5.begin();            // Wire.begin() must be after M5.begin()
      Wire.begin(26, 32);    // Atom Matrix I2C GPIO Pin is 26 and 32 <- Important
      Serial.begin(115200);  // start serial for output
      Serial.println("VLX53LOX test started.");
    void loop() {
      Serial.println("----- START TEST ----");
      Serial.println("----- END TEST ----");
    void test() {
      byte val1 = read_byte_data_at(VL53L0X_REG_IDENTIFICATION_REVISION_ID);
      Serial.print("Revision ID: "); Serial.println(val1);
      val1 = read_byte_data_at(VL53L0X_REG_IDENTIFICATION_MODEL_ID);
      Serial.print("Device ID: "); Serial.println(val1);
      val1 = read_byte_data_at(VL53L0X_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD);
      Serial.print("PRE_RANGE_CONFIG_VCSEL_PERIOD="); Serial.println(val1);
      Serial.print(" decode: "); Serial.println(VL53L0X_decode_vcsel_period(val1));
      val1 = read_byte_data_at(VL53L0X_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD);
      Serial.print("FINAL_RANGE_CONFIG_VCSEL_PERIOD="); Serial.println(val1);
      Serial.print(" decode: "); Serial.println(VL53L0X_decode_vcsel_period(val1));
      write_byte_data_at(VL53L0X_REG_SYSRANGE_START, 0x01);
      byte val = 0;
      int cnt = 0;
      while (cnt < 100) { // 1 second waiting time max
        val = read_byte_data_at(VL53L0X_REG_RESULT_RANGE_STATUS);
        if (val & 0x01) break;
      if (val & 0x01) Serial.println("ready"); else Serial.println("not ready");
      read_block_data_at(0x14, 12);
      uint16_t acnt = makeuint16(gbuf[7], gbuf[6]);
      uint16_t scnt = makeuint16(gbuf[9], gbuf[8]);
      uint16_t dist = makeuint16(gbuf[11], gbuf[10]);
      byte DeviceRangeStatusInternal = ((gbuf[0] & 0x78) >> 3);
      Serial.print("ambient count: "); Serial.println(acnt);
      Serial.print("signal count: ");  Serial.println(scnt);
      Serial.print("distance ");       Serial.println(dist);
      Serial.print("status: ");        Serial.println(DeviceRangeStatusInternal);
    uint16_t bswap(byte b[]) {
      // Big Endian unsigned short to little endian unsigned short
      uint16_t val = ((b[0] << 8) & b[1]);
      return val;
    uint16_t makeuint16(int lsb, int msb) {
      return ((msb & 0xFF) << 8) | (lsb & 0xFF);
    void write_byte_data(byte data) {
    void write_byte_data_at(byte reg, byte data) {
      // write data word at address and register
    void write_word_data_at(byte reg, uint16_t data) {
      // write data word at address and register
      byte b0 = (data & 0xFF);
      byte b1 = ((data >> 8) && 0xFF);
    byte read_byte_data() {
      Wire.requestFrom(address, 1);
      while (Wire.available() < 1) delay(1);
      byte b = Wire.read();
      return b;
    byte read_byte_data_at(byte reg) {
      Wire.requestFrom(address, 1);
      while (Wire.available() < 1) delay(1);
      byte b = Wire.read();
      return b;
    uint16_t read_word_data_at(byte reg) {
      Wire.requestFrom(address, 2);
      while (Wire.available() < 2) delay(1);
      gbuf[0] = Wire.read();
      gbuf[1] = Wire.read();
      return bswap(gbuf);
    void read_block_data_at(byte reg, int sz) {
      int i = 0;
      Wire.requestFrom(address, sz);
      for (i = 0; i < sz; i++) {
        while (Wire.available() < 1) delay(1);
        gbuf[i] = Wire.read();
    uint16_t VL53L0X_decode_vcsel_period(short vcsel_period_reg) {
      // Converts the encoded VCSEL period register value into the real
      // period in PLL clocks
      uint16_t vcsel_period_pclks = (vcsel_period_reg + 1) << 1;
      return vcsel_period_pclks;

  • @madgeometer
    It worked perfectly. This is the serial log I had:

    ----- START TEST ----
    Revision ID: 16
    Device ID: 238
     decode: 16
     decode: 12
    ambient count: 46
    signal count: 7
    distance 1854
    status: 11
    ----- END TEST ----

  • Thanks for your help guys, I'll give this all a shot!