Instructions

Asset Tracker Template

tags
Asset Tracker Template

Before adding a new sensor, make sure the sensor's driver is available in Zephyr RTOS and uses the Zephyr Sensor API. Zephyr includes such a driver for the Bosch BMM350 magnetometer.

  1. Add the sensor to the devicetree and enable it. This step:

    • Instantiates a devicetree node for the sensor.
    • Initializes the driver and the sensor during boot.

    In the case of the Bosch BMM350 magnetometer, the device is already added to the devicetree. The node can be found in the nRF Connect SDK in the nrf/boards/nordic/thingy91x/thingy91x_common.dtsi file.

    To enable the sensor, add the following to the Asset Tracker Template's board-specific devicetree overlay file app/boards/thingy91x_nrf9151_ns.overlay:

    &magnetometer {
        status = "okay";
    };
    
  2. Update the environmental module's state structure in the app/src/modules/environmental/environmental.c file to include the magnetometer device reference and data fields:

    struct environmental_state_object {
        /* ... existing fields ... */
    
        /* BMM350 sensor device reference */
        const struct device *const bmm350;
    
        /* Magnetic field measurements (X, Y, Z) in Gauss */
        double magnetic_field[3];
    };
    
  3. In the module's thread function env_module_thread() in the same file, find the initialization of the environmental_state structure and add the reference to the device using the devicetree label:

    struct environmental_state_object environmental_state = {
        .bme680 = DEVICE_DT_GET(DT_NODELABEL(bme680)),
        .bmm350 = DEVICE_DT_GET(DT_NODELABEL(magnetometer)), /* Add this line */
    };
    
  4. In the same file, update the sensor sampling function signature to include the magnetometer device:

    static void sample_sensors(const struct device *const bme680,
                               const struct device *const bmm350)
    
  5. In the state_running_run() function in the same file, update the call to sample_sensors():

    sample_sensors(state_object->bme680, state_object->bmm350);
    
  6. Update the sample_sensors() function in the same file to sample from the new sensor and add the data to the outgoing message (the message struct is extended in the next step):

    static void sample_sensors(const struct device *const bme680,
                               const struct device *const bmm350)
    {
        /* ... existing code, calls to sensor_sample_fetch() and sensor_channel_get() ... */
    
        struct sensor_value magnetic_field[3] = { {0}, {0}, {0} };
    
        err = sensor_sample_fetch(bmm350);
        if (err) {
            LOG_ERR("Failed to fetch magnetometer sample: %d", err);
            SEND_FATAL_ERROR();
            return;
        }
    
        err = sensor_channel_get(bmm350, SENSOR_CHAN_MAGN_XYZ, magnetic_field);
        if (err) {
            LOG_ERR("Failed to get magnetometer data: %d", err);
            SEND_FATAL_ERROR();
            return;
        }
    
        LOG_DBG("Magnetic field: X: %.2f G, Y: %.2f G, Z: %.2f G",
                sensor_value_to_double(&magnetic_field[0]),
                sensor_value_to_double(&magnetic_field[1]),
                sensor_value_to_double(&magnetic_field[2]));
    
        struct environmental_msg msg = {
            /* ... existing fields ... */
            .magnetic_field[0] = sensor_value_to_double(&magnetic_field[0]),
            .magnetic_field[1] = sensor_value_to_double(&magnetic_field[1]),
            .magnetic_field[2] = sensor_value_to_double(&magnetic_field[2]),
        };
    
        /* ... existing code to timestamp and publish the message ... */
    }
    
  7. Update the environmental_msg structure in app/src/modules/environmental/environmental.h to include the magnetic field data:

    struct environmental_msg {
        /* ... existing fields ... */
    
        /** Magnetic field measurements (X, Y, Z) in Gauss */
        double magnetic_field[3];
    };
    
  8. Add cloud integration in the cloud_environmental_send() function in app/src/modules/cloud/cloud_environmental.c to send magnetometer data to nRF Cloud. There is no predefined application ID for magnetometer data, so the following code uses a custom "MAGNETIC_FIELD" ID. Update the function as follows:

    int cloud_environmental_send(const struct environmental_msg *env,
                                 int64_t timestamp_ms,
                                 bool confirmable)
    {
        int err;
    
        /* ... existing code to send temperature, pressure and humidity ... */
    
        char mag_message[64];
    
        /* Format magnetometer data as a string with three values */
        snprintk(mag_message, sizeof(mag_message),
                 "%.2f %.2f %.2f",
                 env->magnetic_field[0],
                 env->magnetic_field[1],
                 env->magnetic_field[2]);
    
        err = nrf_cloud_coap_message_send("MAGNETIC_FIELD",
                                          mag_message,
                                          false,
                                          timestamp_ms,
                                          confirmable);
        if (err) {
            LOG_ERR("Failed to send magnetometer data to cloud, error: %d", err);
            return err;
        }
    
        LOG_DBG("Magnetometer data sent to cloud: %s", mag_message);
    
        return 0;
    }
    
  9. Build and run the modified application.

  10. Confirm that the custom messages appear in the Terminal card in nRF Cloud, as shown below:

    nRF Cloud magnetometer messages