In this final module, we bring everything together to create a sensor-driven environmental monitoring system using the onboard capabilities of the Nordic Thingy:91X.------------------------------------------------------------------------------------------------------------------
๐ Welcome Aboard MakerTrain: A Hands-on Nordic Thingy:91X WorkshopYou're about to begin an exciting journey using the powerful Nordic Thingy:91X. This is a self-paced, async-style workshop designed for makers, developers, and students who want to learn by building a real-world project step by step.
This workshop is structured across three interconnected Hackster projects:
๐ Workshop Modules:
- ๐ 1๏ธโฃ MakerTrain: Pre-Departure Checklist โ nRF SDK Setup
- ๐ 2๏ธโฃ Introduction to Thingy:91X & nRF Connect SDK โ Learn about the board, run your first app.
- ๐ 3๏ธโฃ Build an Environmental Monitoring System โ Put it all together in a real-world project. (you are here!)
Each module builds on the previous one, so it's best to follow them in order. While this workshop is async (move at your own pace), weโll also walk through each part during the live session.
Let's get started ๐.
------------------------------------------------------------------------------------------------------------------
Our original goal was to transmit live sensor data (temperature, humidity, air quality, and more) over cellular (LTE-M / NB-IoT) to a cloud service via MQTT. However, due to network connectivity challenges during the workshop, weโve adapted the project to work offline, focusing on data collection, sensor integration, and local display/logging.
This doesn't limit the value of the workshopโin fact, it sets the foundation for building full cellular IoT applications. The Thingy:91Xโs real strength lies in its built-in CAT-M and NB- support, and you are encouraged to expand this project after the workshop by enabling network connectivity.
๐ Step 1: Intr odu c tion to Thingy:9 1x Onboard SensorsThe Thingy:91x development kit is equipped with a variety of built-in sensors that support environmental monitoring and motion sensing applications. These sensors are connected via I2C and SPI interfaces and offer precise measurements for various use cases. Below is a list of the onboard sensors and their details:
- BME688 - Environmental Sensor - Ambient temperature, Relative humidity, Atmospheric pressure, Gas/ Indoor air quality estimation based on VoC
- BMI270 - Accelerometer + Gyroscope
- ADXL367 -Low-power Accelerometer
- BMM350 - Magnetometer-
We can find the exact connection and schematics details in the "Thingy:91x Sensors and LED's" datasheet page (PDF file in the attachment)
These sensor are connected both nRF9151 and nRF5340 and we can access it from both controllers.
๐ฆ Objective: Read onboard sensors and print them in the console. The easiest one is scan BMI270 Accelerometer + Gyro values and prints it, Let's get started. Step 2.1: Create a new project with nRF Connect SDK.
If you did't install the nRF Connect SDK, please check this guide - 1๏ธโฃ ๐ MakerTrain: Pre-Departure Checklist - nRF SDK Setup.Click on "Create a new application"
and choose "Create a blank application"
and enter a project name, and here "imu"
Now, we can see the bare metal program and files.
Step 2.2: AddProject Configuration.
To Enable the sensors, we need to add the required configuration in the prj.conf.This is a Kconfig fragment that specifies application-specific values for one or more Kconfig options. These application settings are merged with other settings to produce the final configuration. The purpose of Kconfig fragments is usually to configure the software features used by the application.
Open your prj.conf file and replace with below content
CONFIG_BOOTLOADER_MCUBOOT=y
CONFIG_STDOUT_CONSOLE=y
CONFIG_GPIO=y
CONFIG_SPI=y
CONFIG_SENSOR=y
Step 2.3: Add Base Devicetree overlays for the access the BMI270The Base Devicetree overlays field will list the Devicetree overlays that are available in the template or have been added to the application folder. These are modifiers to your hardware description.
create file named "thingy91x_nrf9151.overlay" under the src/boards, the folder will be not there, so create a new folder boards under the src and add the file. Open your thingy91x_nrf9151.overlay file and replace with below content
&spi3 {
accel_hp: accelerometer_hp: bmi270@2{
status = "okay";
};
};
It will looks like this.
Step 2.4: Update your main.c program file to access the sensor and print on serial. Open your main.c file and replace with below content and save it,
/*
* Copyright (c) 2021 Bosch Sensortec GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <stdio.h>
int main(void)
{
const struct device *const dev = DEVICE_DT_GET_ONE(bosch_bmi270);
struct sensor_value acc[3], gyr[3];
struct sensor_value full_scale, sampling_freq, oversampling;
if (!device_is_ready(dev)) {
printf("Device %s is not ready\n", dev->name);
return 0;
}
printf("Device %p name is %s\n", dev, dev->name);
/* Setting scale in G, due to loss of precision if the SI unit m/s^2
* is used
*/
full_scale.val1 = 2; /* G */
full_scale.val2 = 0;
sampling_freq.val1 = 100; /* Hz. Performance mode */
sampling_freq.val2 = 0;
oversampling.val1 = 1; /* Normal mode */
oversampling.val2 = 0;
sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_FULL_SCALE,
&full_scale);
sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_OVERSAMPLING,
&oversampling);
/* Set sampling frequency last as this also sets the appropriate
* power mode. If already sampling, change to 0.0Hz before changing
* other attributes
*/
sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ,
SENSOR_ATTR_SAMPLING_FREQUENCY,
&sampling_freq);
/* Setting scale in degrees/s to match the sensor scale */
full_scale.val1 = 500; /* dps */
full_scale.val2 = 0;
sampling_freq.val1 = 100; /* Hz. Performance mode */
sampling_freq.val2 = 0;
oversampling.val1 = 1; /* Normal mode */
oversampling.val2 = 0;
sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_FULL_SCALE,
&full_scale);
sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ, SENSOR_ATTR_OVERSAMPLING,
&oversampling);
/* Set sampling frequency last as this also sets the appropriate
* power mode. If already sampling, change sampling frequency to
* 0.0Hz before changing other attributes
*/
sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ,
SENSOR_ATTR_SAMPLING_FREQUENCY,
&sampling_freq);
while (1) {
/* 10ms period, 100Hz Sampling frequency */
k_sleep(K_MSEC(10));
sensor_sample_fetch(dev);
sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, acc);
sensor_channel_get(dev, SENSOR_CHAN_GYRO_XYZ, gyr);
printf("AX: %d.%06d; AY: %d.%06d; AZ: %d.%06d; "
"GX: %d.%06d; GY: %d.%06d; GZ: %d.%06d;\n",
acc[0].val1, acc[0].val2,
acc[1].val1, acc[1].val2,
acc[2].val1, acc[2].val2,
gyr[0].val1, gyr[0].val2,
gyr[1].val1, gyr[1].val2,
gyr[2].val1, gyr[2].val2);
k_sleep(K_MSEC(50));
}
return 0;
}
IMU Code Explanation.๐ง 1. Device Setup
const struct device *const dev = DEVICE_DT_GET_ONE(bosch_bmi270);
- This gets the device handle using the DeviceTree for the BMI270 sensor.
- DEVICE_DT_GET_ONE looks for the first bosch_bmi270 compatible device node.
if (!device_is_ready(dev)) {
printf("Device %s is not ready\n", dev->name);
return 0;
}
- Verifies that the sensor device is initialized and ready to use.
โ๏ธ 2.Sensor Configuration
๐ฆ Accelerometer Configuration
full_scale.val1 = 2; // ยฑ2g range
sampling_freq.val1 = 100; // 100 Hz
oversampling.val1 = 1; // Normal mode
- Configures the accelerometer full scale, sampling rate, and oversampling.
sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_FULL_SCALE, &full_scale);
sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_OVERSAMPLING, &oversampling);
sensor_attr_set(dev, SENSOR_CHAN_ACCEL_XYZ, SENSOR_ATTR_SAMPLING_FREQUENCY, &sampling_freq);
- These three calls apply the above attributes to the acceleration channel.
๐ Gyroscope Configuration
ssfull_scale.val1 = 500; // ยฑ500 degrees/sec
sampling_freq.val1 = 100;
oversampling.val1 = 1;
- Similar setup for the gyroscope channel:
sensor_attr_set(dev, SENSOR_CHAN_GYRO_XYZ, ...);
๐ 3. Data Fetch Loop
while (1) {
k_sleep(K_MSEC(10));
sensor_sample_fetch(dev);
- Every 10ms (100Hz), a new sample is fetched from the sensor
sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, acc);
sensor_channel_get(dev, SENSOR_CHAN_GYRO_XYZ, gyr);
- Reads XYZ values for both accelerometer (acc[]) and gyroscope (gyr[]).
printf("AX: %d.%06d; ... GZ: %d.%06d;\n", ...);
k_sleep(K_MSEC(50));
- Prints all 6 axis readings in fixed-point format (e.g., 1.123456).
- Waits 50ms before repeating (printing at 20Hz, sampling at 100Hz)
1. click on the NCS logo (Nordic Connect SDK) 2. Choose the "Add build configuration" option.
here, we are going mention the SDK type, Toolchain version, board target, base configuration and base devicetree overlays we made.
1. SDK - Choose 2.90 or above - In the 1๏ธโฃ ๐ MakerTrain: Pre-Departure Checklist - nRF SDK Setup Step we installed the 3.0.0 - that will be enough. 2. Toolchain - Choose 2.9.0 or above - In the 1๏ธโฃ ๐ MakerTrain: Pre-Departure Checklist - nRF SDK Setup Step we installed the 3.0.0 - that will be enough.
3. Board Target - thingy91x/nrf9151/ns type few letters and then you can choose. โ ๏ธ This is very important, I almost bricked my Thing:91X. and revived. 4. Base configuration files - choose the prj.conf we have from Step 2.2: AddProject Configuration.
5. Base Devicetree Overlays - choose the thingy91x_nrf9151.overlay we have from Step 2.3: Add Base Devicetree overlays for the access the BMI270
๐ฆ Generate and BuildOnce the "Create Build Configuration" is done, click Generate and Build.
It will take few seconds to minutes depends on the system and files we have.
At the end, if you see like this, then all OK ๐. otherwise, there will be error during the build - so we need to investigation. Don't worry if you get error, that's normal and we can fix it.
โจ Flash to the Thingy:91X BoardFirst, Enter the following command to list the connected devices and their traits:
nrfutil device list
Output should be
THINGY91X_863474028C2
Product Thingy:91 X UART
Ports /dev/tty.usbmodem12102, vcom: 0
/dev/tty.usbmodem12105, vcom: 1
Traits mcuBoot, modem, serialPorts, usb, nordicUsb
Supported devices found: 1
The Nordic Thingy:91 X will be listed as a Thingy:91 X UART product and have the following details: mcuboot, nordicUsb, serialPorts, and usb traits.
A 21-character J-Link serial number, for example, THINGY91X_863474028C2
Flash the program with command
Enter the following command to program the application binary to the nRF9151 application core:
nrfutil device program --firmware dfu_application.zip --serial-number <J-Link Serial number> --traits mcuboot --x-family nrf91 --core Application
replace the<J-Link Serial number> with your nRF:91x Serial number obtained from "Step 2.4 Grab the nRF:91x Serial Code"For example
nrfutil device program --firmware dfu_application.zip --serial-number THINGY91X_863474028C2 --traits mcuboot --x-family nrf91 --core Application
If the flashing is successful, you can see the output in the terminal.
[00:00:07] ###### 100% [4/4 THINGY91X_863474028C2] Programmed
๐ฅฝ Read the data using nRF Serial TerminalOpen the nRF Connect for Desktop and then open the "Serial Terminal".
Choose the device from the device list - make sure your device is connected
You can see the accelerometer and gyroscope values streaming in the serial.
Here is a gif (I like gif ๐)
โ Learn about Thingy:91x Internal Sensors
โ Learn how to write program to access the Internal Sensors
โ Lean how to access the data via Serial Terminal.
๐ Next the Main Project - Track Air: An Envi ronmental Monitoring SystemThis is a comparatively big project, so we are not doing all the code by hand - we will import the project and then flash it.
The true strength of the Thingy:91X lies in its NB-IoT and CAT-M cellular connectivity. It comes preloaded with firmware that can stream sensor data to nRF Cloud right out of the box. However, since the workshop is taking place on a moving train with uncertain cellular coverage, weโve decided to fetch the sensor data over Wi-Fi and display it locally in a web browser instead.
There is an problem, the Thing:91x WiFi module - nRF7002 originally made only to scan the WiFi AP to get location information. But Since, we are modifying it - we need to remove everything from the flash - In the nRF9151, nRF5340 and nRF7002. so please do this carefully and we need an external debugger as the Thing:91x does't have one. During the workshop, I can help in person and make everything right.
In this project - we have two method to flash the code, one is using the hex file with nRF programmer and then using the nRF connection sdk building and flashing the code. I will document both and you can choose depends on the time. One flashing is done, please try to load the factory firmware - otherwise it will difficult to access via MCUBoot (bootloader) and can't do the direct usb programmingMethod 1: Use the.hex firmware file.๐ธ๏ธ Step 1: Download.hex firmware file
Click here to download the.hex firmware file first
Step 2: Connect the Thingy:91x with Segger J-Link and Erase the Flash for nRF9151 and nRF5340
Connect with J-Link cable to the Thingy:91x J-Tag port.
Orientation is very important, there is notch in the case, make sure that match with cable notch and only connect when the Thing:91X is OFF.
๐งฐ Step2.2 : Erase the nRF91 Flash first
Connect the JLINK to the computer and power the Thingy:91x also and put the SWD in the Thingy:91x to nRF91 mode.
Then open the Programmer from the nRF Connect for Desktop.
Choose the J-Link_SLite or whatever model you have
1. Make sure you are connected to J-LINK 2. Make sure you have "NRF9151" listed on the right side
3. Then finally click Erase all
Once erased, you can see the empty memory area.
๐งฐ Step 2.3: Erasethe nRF52 Flash
First Turn off the Thingy and then - put the SWD in the Thingy:91x to nRF53 mode.
Turn on Thingy again and open the nRF Programmer.make sure you have the "NRF5340" selected. the click Erase All (3)
Once it's done, you can see the empty place.
Now we erase both nRF52 and nRF91 flashes, let's add our project firmware. โ ๏ธ Before loading the firware, make sure your Thingy:91x is nRF53 SWD mode.
Step 2:1 Add Firmware
Click on Add File and choose the.hex file you downloaded.
After loading the.hex file, you can see the File memory layout window, next click on "Erase & Write" to load the firmware.
You can see the flashing logs below.
After flashing you can see the nRF53 memory is loaded with firmware.
If it's successfully flashed you can see RED light is turned on.
In order to access the web page, first we need to configure the WiFi network. For that, let's open Serial Terminal
and select the board
And Then, you can see the WiFi Access Point List
๐ก Step 3.2: Connect to your WiFi AP. For the MakerTrain, we arranged a WiFi AP.
WiFi Name: MakerTrain Thingy91x
Password: Hackster
to enter the WiFi details, use the top side window, type and enter.
The String format is
wifi_cred add -s <SSID> -k 1 -p <PASSWORD>
and for the Maker Train Event - Use
wifi_cred add -s 'MakerTrain Thingy91x' -k 1 -p 'Hackster'
After that, just reboot the Thingy to see the IP address on the Serial Terminal.
๐ฅณ Step 4: Access the Dashboard
In order to access the dashboard, you also need to be the same network, so please connect your computer also in the same network.
WiFi Name: MakerTrain Thingy91x
Password: Hackster
Then, open a browser and see the Dashboard ๐ค.
Yeyye, It's working ๐ฅณ.
Demo Video.Explore The Features 1. You can pick anywhere in the color wheel and based on that, the Thingy LED will turn on. 2. We can see the Thingy:91x Orientation Live, there is an option to reset also. 3. We can grab the Location with WiFi SSID - for that we need to enter the JWT Token - I will explain that next steps.
You can click here to download the code and then extract the files to save it somewhere safe.
Step 1: ๐ฆ Load the project and Check the codeOpen a new window in VS Code, file -> New Window and the click the NCS Logo to open the tool and select the "Open an existing application"
Choose the folder and open
Under the src directory, you can see the different modules of code.
Main Application (main.c)
The main application serves as the central coordinator of the system. It initializes all subsystems, manages the application loop, and handles inter-component communication. The code implements LED control, WebSocket data transmission, button handling, and HTTP request processing.
Sensor Management (sensors.c/h)
This module manages all onboard sensors and their data collection. It provides functions for sensor initialization, data collection, and JSON formatting. The system collects 17 different measurements including timestamp, IMU data, accelerometer readings, environmental data, and magnetometer values.
Network Stack (wifi.c/h)
The network stack handles all WiFi-related functionality. It manages network connections, credential storage, and connection state monitoring. The module implements secure credential storage and automatic reconnection capabilities.
Web Server (http_resources.c/h)
The web server component serves the user interface and manages HTTP endpoints. It handles static web resources, implements WebSocket connections for real-time data streaming, and processes HTTP requests for device control and data access.
HTTPS Client (https_request.c/h)
This module manages secure communications with external services. It implements HTTPS connections to nRF Cloud for location services and handles SSL/TLS security protocols. The client manages JWT tokens and certificate verification.
Build the projectClick on the Add build configuration
here, we need choose the "thingy91x/nRF5340/cpuapp" as Board target - it's very important.
And the build the code, after that - you can flash but before that you need to Erase nRF91 Flash and nRF52 Flash Memory. Go to to and follow the Step2.2 : Erase the nRF91 Flash first and then ๐งฐ Step 2.3: Erasethe nRF52 Flash before further continues. After that first connect the device with nRF Connect SDK using the Connected Device tool and refresh to see the device first (1) and the click "Flash" to upload the program (2).
After these steps, please follow "๐ Step 3: Configure WiFi Network" and complete the WiFi Setup" Step and then access the Dashboard.
๐ฅณ Dashboard Demo.โป๏ธ Switching back to factory firmwareif you want to use with default firmware where you can easily connect with nRF cloud and all and you don't have programmer in your hand, you need to load the default firmware.For that, Download the thingy91x_mfw-2.0.2_sdk-2.9.0-preview and flash it on thingy:91x. In the img_app_bl folder,
Use the "thingy91x_hello.nrfcloud.com_v2.0.1.hex" flash to the nRF91 and use "thingy91x_nrf53_connectivity_bridge_v2.0.1.hex" flash to the nRF53
Congratulations ๐ on completing the MakerTrain workshop! ๐๐กWe hope this hands-on experience with the Nordic Thingy:91X and the nRF Connect SDK gave you a solid start in exploring Nordic nRF Controllers and Sensor-based applications.
Weโd love to hear your thoughts!๐ Please share your feedback and suggestions as a comment under this project. Let us know what worked well, what didnโt, and what youโd love to see in future workshops. Your input helps us improve! ๐
And donโt forget to like this project if you found it helpful! ๐Thank you for being part of Maker Train ๐! Whatโs Next?
If you're excited to learn more, we highly recommend diving deeper with Nordicโs official learning resources:
๐ Nordic Developer Academy โ Free online courses on Bluetooth, Cellular IoT, nRF Connect SDK, and more.
๐ Publish your project on Hackster.io โ Inspire others by sharing what you built during the workshop. Make sure to tag it with Thingy:91X and MakerTrain
๐ฌ Join our Hackster community on Discord โ Connect with other makers, ask questions, or share your progress anytime:
Start where you are. Use what you have. Do what you can. Share what you know ๐
Comments