### Cross-Platform MPU6050 STM32 HAL C++ Source: https://context7.com/jrowberg/i2cdevlib/llms.txt This example shows using I2Cdevlib on STM32 microcontroller with HAL libraries to interface with MPU6050 sensor. It depends on STM32 HAL libraries, i2cdev_hal.h, and mpu6050_hal.h; initializes I2C and reads motion data in a loop. Outputs accelerometer and gyroscope values via UART. Limitation: Requires STM32 HAL configuration and proper I2C/UART setup in the project. ```cpp // STM32 HAL platform example #include "i2cdev_hal.h" #include "mpu6050_hal.h" extern I2C_HandleTypeDef hi2c1; int main(void) { HAL_Init(); SystemClock_Config(); MX_I2C1_Init(); MX_USART2_UART_Init(); // Initialize I2Cdev with HAL I2C handle I2Cdev_init(&hi2c1); // Initialize MPU6050 MPU6050_initialize(); // Test connection uint8_t connected = MPU6050_testConnection(); if (connected) { printf("MPU6050 connected\r\n"); } int16_t ax, ay, az, gx, gy, gz; while (1) { // Read motion data MPU6050_getMotion6(&ax, &ay, &az, &gx, &gy, &gz); printf("a/g: %d\t%d\t%d\t%d\t%d\t%d\r\n", ax, ay, az, gx, gy, gz); HAL_Delay(100); } } ``` -------------------------------- ### CMake Project Setup for MPU6050 Calibration on Pico Source: https://github.com/jrowberg/i2cdevlib/blob/master/RP2040/MPU6050/examples/mpu6050_calibration/CMakeLists.txt This CMakeLists.txt file configures a Raspberry Pi Pico project. It sets the minimum CMake version, includes the Pico SDK, defines the project name and C/C++ standards, and initializes the SDK. It then defines the executable target, including source files and necessary include directories. ```cmake cmake_minimum_required(VERSION 3.13) # Pull in SDK (must be before project) # set(PICO_BOARD pico_w) # Needed only for PICO W # set(CYW43_LWIP 1) # Needed only for PICO W include(pico_sdk_import.cmake) project(mpu6050_calibration C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) # Initialise the Raspberry Pi Pico SDK pico_sdk_init() # Add executable. Default name is the project name, version 0.1 add_executable( mpu6050_calibration mpu6050_calibration.cpp ../../MPU6050.cpp ../../../I2Cdev/I2Cdev.cpp ) target_include_directories(mpu6050_calibration PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../ ${CMAKE_CURRENT_LIST_DIR}/../../../I2Cdev ) # Add any user requested libraries target_link_libraries( mpu6050_calibration pico_stdlib # pico_cyw43_arch_none # we need Wifi to access the GPIO. Needed only for PICO W hardware_i2c ) pico_set_program_name(mpu6050_calibration "mpu6050_calibration") pico_set_program_version(mpu6050_calibration "0.1") pico_enable_stdio_uart(mpu6050_calibration 0) pico_enable_stdio_usb(mpu6050_calibration 1) # create map/bin/hex file etc. pico_add_extra_outputs( mpu6050_calibration) ``` -------------------------------- ### CMake Configuration for Raspberry Pi Pico Project Source: https://github.com/jrowberg/i2cdevlib/blob/master/RP2040/MPU6050/examples/mpu6050_DMP_V6.12/CMakeLists.txt This snippet shows the CMake configuration for a Raspberry Pi Pico project. It sets up the minimum CMake version, includes the Pico SDK, defines project settings, and adds the main executable with its source files and dependencies. It also configures standard I/O and output formats. ```cmake cmake_minimum_required(VERSION 3.13) # Pull in SDK (must be before project) # set(PICO_BOARD pico_w) # Needed only for PICO W # set(CYW43_LWIP 1) # Needed only for PICO W include(pico_sdk_import.cmake) project(mpu6050_DMP_port C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) # Initialise the Raspberry Pi Pico SDK pico_sdk_init() # Add executable. Default name is the project name, version 0.1 add_executable(mpu6050_DMP_port mpu6050_DMP_port.cpp ../../MPU6050.cpp ../../../I2Cdev/I2Cdev.cpp ) target_include_directories(mpu6050_DMP_port PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../ ${CMAKE_CURRENT_LIST_DIR}/../../../I2Cdev ) # Add any user requested libraries target_link_libraries(mpu6050_DMP_port pico_stdlib # pico_cyw43_arch_none # we need Wifi to access the GPIO. Needed only for PICO W hardware_i2c pico_double ) pico_set_program_name(mpu6050_DMP_port "mpu6050_DMP_port") pico_set_program_version(mpu6050_DMP_port "0.1") pico_enable_stdio_uart(mpu6050_DMP_port 0) pico_enable_stdio_usb(mpu6050_DMP_port 1) # create map/bin/hex file etc. pico_add_extra_outputs(mpu6050_DMP_port) ``` -------------------------------- ### Initialize and Read MPU6050 Accelerometer and Gyroscope Source: https://context7.com/jrowberg/i2cdevlib/llms.txt This code initializes the MPU6050 sensor, configures its accelerometer and gyroscope ranges, and reads raw motion data. It verifies the device connection and provides example values for calibration offsets. The code is suitable for motion tracking and robotics applications. ```cpp #include "I2Cdev.h" #include "MPU6050.h" #include "Wire.h" MPU6050 accelgyro; // MPU6050 accelgyro(0x69); // Use if AD0 pin is HIGH // MPU6050 accelgyro(0x68, &Wire1); // Use alternate I2C bus int16_t ax, ay, az; // Accelerometer raw values int16_t gx, gy, gz; // Gyroscope raw values void setup() { Wire.begin(); Serial.begin(38400); // Initialize device with default settings accelgyro.initialize(); // Verify connection if (accelgyro.testConnection()) { Serial.println("MPU6050 connected"); } else { Serial.println("MPU6050 connection failed"); while(1); } // Configure accelerometer range: ±2g, ±4g, ±8g, ±16g accelgyro.setFullScaleAccelRange(MPU6050_ACCEL_FS_2); // Configure gyroscope range: ±250, ±500, ±1000, ±2000 °/s accelgyro.setFullScaleGyroRange(MPU6050_GYRO_FS_250); // Set calibration offsets (obtain from calibration routine) accelgyro.setXAccelOffset(-76); accelgyro.setYAccelOffset(-2359); accelgyro.setZAccelOffset(1688); accelgyro.setXGyroOffset(220); accelgyro.setYGyroOffset(76); accelgyro.setZGyroOffset(-85); } void loop() { // Read all 6 axis at once (most efficient) accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); // Or read separately // accelgyro.getAcceleration(&ax, &ay, &az); // accelgyro.getRotation(&gx, &gy, &gz); // Convert to physical units (for ±2g and ±250°/s ranges) float accel_x = ax / 16384.0; // g float accel_y = ay / 16384.0; float accel_z = az / 16384.0; float gyro_x = gx / 131.0; // °/s float gyro_y = gy / 131.0; float gyro_z = gz / 131.0; Serial.print("a/g:\t"); Serial.print(ax); Serial.print("\t"); Serial.print(ay); Serial.print("\t"); Serial.print(az); Serial.print("\t"); Serial.print(gx); Serial.print("\t"); Serial.print(gy); Serial.print("\t"); Serial.println(gz); // Check for data overflow if (accelgyro.getIntDataReadyStatus()) { // New data available } delay(100); } ``` -------------------------------- ### BMP180 Example in C for STM32 Source: https://github.com/jrowberg/i2cdevlib/blob/master/STM32HAL/README.md This code snippet demonstrates the usage of the BMP180 sensor with the STM32F429I-DISCO board. It initializes the I2C interface, initializes the BMP085 sensor, and continuously reads temperature, pressure, and altitude values. ```C #include "stm32f4xx.h" #include "stm32f4xx_hal.h" #include #include #include #include "I2Cdev.h" #include "BMP085.h" I2C_HandleTypeDef hi2c3; int main(void) { SystemInit(); HAL_Init(); GPIO_InitTypeDef GPIO_InitStruct; /*I2C3 GPIO Configuration PC9 ------> I2C3_SDA PA8 ------> I2C3_SCL */ __GPIOA_CLK_ENABLE(); __GPIOC_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FAST; GPIO_InitStruct.Alternate = GPIO_AF4_I2C3; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FAST; GPIO_InitStruct.Alternate = GPIO_AF4_I2C3; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); __I2C3_CLK_ENABLE(); hi2c3.Instance = I2C3; hi2c3.Init.ClockSpeed = 400000; hi2c3.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c3.Init.OwnAddress1 = 0x10; hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED; hi2c3.Init.OwnAddress2 = 0x11; hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED; hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED; HAL_I2C_Init(&hi2c3); I2Cdev_init(&hi2c3); // init of i2cdevlib. // You can select other i2c device anytime and // call the same driver functions on other sensors while(!BMP085_testConnection()) ; BMP085_initialize(); char buf[128]; while (1) { BMP085_setControl(BMP085_MODE_TEMPERATURE); HAL_Delay(BMP085_getMeasureDelayMilliseconds(BMP085_MODE_TEMPERATURE)); float t = BMP085_getTemperatureC(); BMP085_setControl(BMP085_MODE_PRESSURE_3); HAL_Delay(BMP085_getMeasureDelayMilliseconds(BMP085_MODE_PRESSURE_3)); float p = BMP085_getPressure(); float a = BMP085_getAltitude(p, 101325); } } void SysTick_Handler() { HAL_IncTick(); HAL_SYSTICK_IRQHandler(); } ``` -------------------------------- ### Custom Device Driver Template C++ Source: https://context7.com/jrowberg/i2cdevlib/llms.txt This header file template illustrates the structure for creating a new I2C device driver using I2Cdev abstraction. It includes class definition with methods for configuration and data access. Depends on I2Cdev.h library. Provides placeholders for register addresses and bit manipulations. Limitation: Requires implementation of class methods in a corresponding .cpp file. ```cpp // NewDevice.h #ifndef _NEWDEVICE_H_ #define _NEWDEVICE_H_ #include "I2Cdev.h" // Register addresses #define NEWDEVICE_ADDRESS_DEFAULT 0x50 #define NEWDEVICE_RA_WHO_AM_I 0x00 #define NEWDEVICE_RA_CONFIG 0x01 #define NEWDEVICE_RA_DATA_START 0x10 // Configuration bits #define NEWDEVICE_CFG_ENABLE_BIT 7 #define NEWDEVICE_CFG_MODE_BIT 5 #define NEWDEVICE_CFG_MODE_LENGTH 2 class NewDevice { public: NewDevice(uint8_t address = NEWDEVICE_ADDRESS_DEFAULT, void *wireObj = 0); void initialize(); bool testConnection(); // Configuration methods uint8_t getConfig(); void setConfig(uint8_t config); bool getEnabled(); void setEnabled(bool enabled); uint8_t getMode(); void setMode(uint8_t mode); // Data access methods void getData(int16_t *x, int16_t *y, int16_t *z); int16_t getDataX(); int16_t getDataY(); int16_t getDataZ(); private: uint8_t devAddr; void *wireObj; uint8_t buffer[6]; }; #endif ``` -------------------------------- ### Initialize and Read HMC5883L 3-Axis Digital Compass Source: https://context7.com/jrowberg/i2cdevlib/llms.txt This code snippet demonstrates how to initialize the HMC5883L digital compass, set its measurement mode, gain, data rate, and sample averaging. It reads raw magnetometer data and calculates the compass heading in degrees, applying a declination angle for accurate navigation. ```cpp #include "Wire.h" #include "I2Cdev.h" #include "HMC5883L.h" HMC5883L mag; int16_t mx, my, mz; void setup() { Wire.begin(); Serial.begin(38400); // Initialize with default settings mag.initialize(); // Verify connection Serial.println(mag.testConnection() ? "HMC5883L connected" : "HMC5883L connection failed"); // Configure measurement mode mag.setMode(HMC5883L_MODE_CONTINUOUS); // Continuous measurement // mag.setMode(HMC5883L_MODE_SINGLE); // Single measurement mode // Set gain (affects measurement range and resolution) // HMC5883L_GAIN_1370: ±0.88 Ga, HMC5883L_GAIN_1090: ±1.3 Ga (default) // HMC5883L_GAIN_820: ±1.9 Ga, HMC5883L_GAIN_660: ±2.5 Ga, etc. mag.setGain(HMC5883L_GAIN_1090); // Set data output rate (0.75Hz to 75Hz) mag.setDataRate(HMC5883L_RATE_15); // 15 Hz // Set measurement configuration mag.setSampleAveraging(HMC5883L_AVERAGING_8); // Average 8 samples per output } void loop() { // Read raw magnetometer values mag.getHeading(&mx, &my, &mz); Serial.print("mag:\t"); Serial.print(mx); Serial.print("\t"); Serial.print(my); Serial.print("\t"); Serial.print(mz); Serial.print("\t"); // Calculate heading in degrees (0° = North) float heading = atan2(my, mx); if (heading < 0) { heading += 2 * M_PI; } float headingDegrees = heading * 180 / M_PI; // Apply declination angle (depends on location) // Example: Declination = -13.7° (West) float declination = -13.7; headingDegrees += declination; if (headingDegrees < 0) headingDegrees += 360; if (headingDegrees >= 360) headingDegrees -= 360; Serial.print("heading:\t"); Serial.println(headingDegrees); delay(100); } ``` -------------------------------- ### Read temperature, pressure, and altitude from BMP085 using Arduino C++ Source: https://context7.com/jrowberg/i2cdevlib/llms.txt The snippet initializes the BMP085 sensor via the I2Cdev library, reads calibrated temperature and pressure values, and computes altitude. It demonstrates selecting oversampling modes, handling conversion delays, and outputting results over Serial. Requires the Wire, I2Cdev, and BMP085 libraries. ```cpp #include \"Wire.h\" #include \"I2Cdev.h\" #include \"BMP085.h\" BMP085 barometer; float temperature; float pressure; float altitude;\nvoid setup() { Wire.begin(); Serial.begin(38400); // Initialize with default settings barometer.initialize(); // Verify connection Serial.println(barometer.testConnection() ? \"BMP085 connected\" : \"BMP085 connection failed\"); } void loop() { // Request temperature measurement barometer.setControl(BMP085_MODE_TEMPERATURE); delay(5); // Wait 4.5ms for conversion // Read calibrated temperature in Celsius temperature = barometer.getTemperatureC(); // float tempF = barometer.getTemperatureF(); // Or Fahrenheit // Request pressure measurement with oversampling // BMP085_MODE_PRESSURE_0: Ultra low power (4.5ms, ±3 hPa) // BMP085_MODE_PRESSURE_1: Standard (7.5ms, ±2 hPa) // BMP085_MODE_PRESSURE_2: High resolution (13.5ms, ±1.5 hPa) // BMP085_MODE_PRESSURE_3 Ultra high resolution (25.5ms, ±1 hPa) barometer.setControl(BMP085_MODE_PRESSURE_3); delay(26); // Wait for conversion (varies by mode) // Read calibrated pressure in Pascals (Pa) pressure = barometer.getPressure(); float pressureHPa = pressure / 100.0; // Convert to hPa/mbar float pressureInHg = pressure / 3386.39; // Convert to inHg // Calculate altitude in meters based on pressure // Default sea level pressure: 101325 Pa altitude = barometer.getAltitude(pressure); // Or specify local sea level pressure for better accuracy float seaLevelPressure = 101500; // Pa (varies by weather) altitude = barometer.getAltitude(pressure, seaLevelPressure); Serial.print(\"T: \"); Serial.print(temperature); Serial.print(\"°C\t\"); Serial.print(\"P: \"); Serial.print(pressureHPa); Serial.print(\" hPa\t\"); Serial.print(\"Alt: \"); Serial.print(altitude); Serial.println(\" m\"); delay(1000); } ``` -------------------------------- ### I2Cdev Low-Level I2C Communication Operations in C++ Source: https://context7.com/jrowberg/i2cdevlib/llms.txt Demonstrates core I2Cdev class static methods for low-level I2C operations including reading/writing bytes, reading multiple bytes, and manipulating specific bits in registers. Shows usage of configurable timeouts and custom Wire objects for multi-bus configurations. Requires I2Cdev.h and Wire.h headers, works across Arduino, ESP32, STM32, and other supported platforms. ```cpp #include "I2Cdev.h" #include "Wire.h" void setup() { Wire.begin(); uint8_t deviceAddr = 0x68; uint8_t regAddr = 0x75; // WHO_AM_I register uint8_t data; // Read a single byte from device register int8_t status = I2Cdev::readByte(deviceAddr, regAddr, &data); if (status > 0) { Serial.print("Device ID: 0x"); Serial.println(data, HEX); } // Write a byte to a register bool success = I2Cdev::writeByte(deviceAddr, 0x6B, 0x00); // Wake up device // Read multiple bytes uint8_t buffer[6]; I2Cdev::readBytes(deviceAddr, 0x3B, 6, buffer); // Read 6 bytes starting at 0x3B // Read specific bits from a register uint8_t bitValue; I2Cdev::readBits(deviceAddr, 0x1A, 5, 3, &bitValue); // Read 3 bits starting at bit 5 // Write specific bits to a register I2Cdev::writeBits(deviceAddr, 0x1B, 4, 2, 0x02); // Write 0x02 to bits 4-3 // Custom timeout (default 1000ms) I2Cdev::readByte(deviceAddr, regAddr, &data, 500); // 500ms timeout // Using a custom Wire object (e.g., Wire1) I2Cdev::readByte(deviceAddr, regAddr, &data, I2Cdev::readTimeout, &Wire1); } void loop() {} ``` -------------------------------- ### Configuring and Reading ADS1115 ADC in Arduino (C++) Source: https://context7.com/jrowberg/i2cdevlib/llms.txt This snippet demonstrates initializing the ADS1115 ADC, setting modes, gain, rate, and reading single-ended and differential voltages. It requires the Wire and ADS1115 libraries, with inputs via analog pins and outputs as voltage values via Serial. Limitations include I2C addressing and polling timeouts for conversion readiness. ```cpp #include "Wire.h" #include "ADS1115.h" ADS1115 adc0(ADS1115_DEFAULT_ADDRESS); // 0x48 // ADS1115 adc1(0x49); // Use if ADDR pin connected to VDD const int alertReadyPin = 2; void setup() { Wire.begin(); Serial.begin(115200); // Verify connection Serial.println(adc0.testConnection() ? "ADS1115 connected" : "ADS1115 connection failed"); adc0.initialize(); // Set conversion mode adc0.setMode(ADS1115_MODE_SINGLESHOT); // Single conversion per request // adc0.setMode(ADS1115_MODE_CONTINUOUS); // Continuous conversion // Set sample rate (8 to 860 SPS) // ADS1115_RATE_8, ADS1115_RATE_16, ADS1115_RATE_32, ADS1115_RATE_64, // ADS1115_RATE_128, ADS1115_RATE_250, ADS1115_RATE_475, ADS1115_RATE_860 adc0.setRate(ADS1115_RATE_128); // Set programmable gain amplifier (PGA) // ADS1115_PGA_6P144: ±6.144V (default), ADS1115_PGA_4P096: ±4.096V // ADS1115_PGA_2P048: ±2.048V, ADS1115_PGA_1P024: ±1.024V // ADS1115_PGA_0P512: ±0.512V, ADS1115_PGA_0P256: ±0.256V adc0.setGain(ADS1115_PGA_4P096); // ±4.096V range // Configure ALERT/RDY pin as conversion ready indicator pinMode(alertReadyPin, INPUT_PULLUP); adc0.setConversionReadyPinMode(); } void pollConversionReady() { for (uint32_t i = 0; i < 100000; i++) { if (!digitalRead(alertReadyPin)) return; } Serial.println("Conversion timeout!"); } void loop() { // Single-ended measurements (input vs GND) // ADS1115_MUX_P0_NG: A0 vs GND, ADS1115_MUX_P1_NG: A1 vs GND, etc. adc0.setMultiplexer(ADS1115_MUX_P0_NG); adc0.triggerConversion(); pollConversionReady(); float voltage0 = adc0.getMilliVolts(false) / 1000.0; // Convert to volts Serial.print("A0: "); Serial.print(voltage0, 4); Serial.print("V "); adc0.setMultiplexer(ADS1115_MUX_P1_NG); adc0.triggerConversion(); pollConversionReady(); float voltage1 = adc0.getMilliVolts(false) / 1000.0; Serial.print("A1: "); Serial.print(voltage1, 4); Serial.println("V"); // Differential measurements // ADS1115_MUX_P0_N1: A0 - A1, ADS1115_MUX_P2_N3: A2 - A3 adc0.setMultiplexer(ADS1115_MUX_P0_N1); adc0.triggerConversion(); // Poll via I2C instead of pin (pass true to getMilliVolts) float diffVoltage = adc0.getMilliVolts(true) / 1000.0; Serial.print("Differential A0-A1: "); Serial.print(diffVoltage, 4); Serial.println("V"); // Read raw ADC value int16_t rawValue = adc0.getConversion(); delay(500); } ``` -------------------------------- ### Multiple I2C Bus Support Arduino C++ Source: https://context7.com/jrowberg/i2cdevlib/llms.txt This Arduino sketch demonstrates connecting multiple MPU6050 sensors on different I2C buses using Wire objects, allowing devices with the same address on separate buses. It requires I2Cdev.h, MPU6050.h, and Wire.h libraries; initializes four IMUs and continuously reads accelerometer data. Limitation: Assumes the Arduino board supports multiple I2C buses like Wire1. ```cpp #include "I2Cdev.h" #include "MPU6050.h" #include "Wire.h" // Create four MPU6050 instances on different buses MPU6050 imu1(0x68, &Wire); // First I2C bus, AD0 low MPU6050 imu2(0x69, &Wire); // First I2C bus, AD0 high MPU6050 imu3(0x68, &Wire1); // Second I2C bus, AD0 low MPU6050 imu4(0x69, &Wire1); // Second I2C bus, AD0 high int16_t ax1, ay1, az1, gx1, gy1, gz1; int16_t ax2, ay2, az2, gx2, gy2, gz2; int16_t ax3, ay3, az3, gx3, gy3, gz3; int16_t ax4, ay4, az4, gx4, gy4, gz4; void setup() { // Initialize both I2C buses Wire.begin(); Wire1.begin(); Serial.begin(115200); // Initialize all four IMUs imu1.initialize(); imu2.initialize(); imu3.initialize(); imu4.initialize(); // Test connections Serial.print("IMU1: "); Serial.println(imu1.testConnection() ? "OK" : "FAIL"); Serial.print("IMU2: "); Serial.println(imu2.testConnection() ? "OK" : "FAIL"); Serial.print("IMU3: "); Serial.println(imu3.testConnection() ? "OK" : "FAIL"); Serial.print("IMU4: "); Serial.println(imu4.testConnection() ? "OK" : "FAIL"); } void loop() { // Read from all four IMUs imu1.getMotion6(&ax1, &ay1, &az1, &gx1, &gy1, &gz1); imu2.getMotion6(&ax2, &ay2, &az2, &gx2, &gy2, &gz2); imu3.getMotion6(&ax3, &ay3, &az3, &gx3, &gy3, &gz3); imu4.getMotion6(&ax4, &ay4, &az4, &gx4, &gy4, &gz4); // Process data from all sensors Serial.print("IMU1 Accel: "); Serial.print(ax1); Serial.print(","); Serial.print(ay1); Serial.print(","); Serial.println(az1); Serial.print("IMU2 Accel: "); Serial.print(ax2); Serial.print(","); Serial.print(ay2); Serial.print(","); Serial.println(az2); Serial.print("IMU3 Accel: "); Serial.print(ax3); Serial.print(","); Serial.print(ay3); Serial.print(","); Serial.println(az3); Serial.print("IMU4 Accel: "); Serial.print(ax4); Serial.print(","); Serial.print(ay4); Serial.print(","); Serial.println(az4); delay(100); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.