In this project will be developed matter device that capable to read data from sensor and deliver the data through wireless connection. Data from matter device will be read directly, and shown in Chiptool data payload.
From the hardware side, data from sensor will be converted from Analog to Digital using embedded ADC NRF7002-DK. Measured data will be stored in internal memory that reserved for particular Matter Cluster (level control cluster), and then will be loaded when there is request to read the data from the platform (Chiptool).
On the platform side, Chiptool will be utilized to acquire data from matter device by sending specific command to device through Wifi. Feedback data will be received on the terminal, and with simple script code will be able to parse and read sensor data easily and understandable.
System Diagram
Wifi and BLE from PC will be utilized by Chiptool to do the provisioning the matter device. After provisioning successful, Matter device will save the history upon next data transfer so provisioning will be needed once, and exchange data from Matter Device to Chiptool on PC will be performed through WIFI.
HardwareThe Hardware that being used in this project are:
- NRF7002-DK
- Soil Moisture Sensor v2.0
- BLE and WIFI dongle for PC
- WIFI 6 Access Point
- Micro USB Cable for the NRF7002-DK
Hardware schematic
The board and soil sensor are connected using colorized jumper cable. Red color is for Power input, it is supplied by 5V and connected to 5V pin on NRF7002-DK. Black cable is for ground, and yellow is for Analog Data connected to A0 input pin.
Software and FirmwareThis project is developed under Linux (Ubuntu) environment, it is required to install ubuntu or other linux distro before deploying the project. Visual Studio Code (VSC) is used as compiler. To be able to compile NRF Firmware, it needs to install NRF extension on VSC. A Jlink driver might need to be installed under the OS hardware drive to able to detect onboard JLink.
Chiptool is mandatory to rebuild the project. Chiptool will be act as the controller from PC side. Without this software, we are unable to do provisioning and requesting data from Matter Device. For latest chiptool, please download from this link. Meanwhile chiptool that used to develop this project is available in repository.
NRF Flash tools might be needed when comes to flashing and erasing data. From this project was found out that deep clean might be needed to fully clean the firmware from flash, which VSCode NRF extension couldn't do.
Firmware Flowchart
Main firmware flowchart is started from initialize NRF 5340 and 7002 peripherals like ADC, GPIO, BLE and Wifi. Continued with initialize the Matter Firmware like Endpoints, Cluster. All of the process are split into some event and task on Zephyr OS library. After all initialized without error, the firmware will run the thread and events dispatch.
To be able to run Analog to Digital converter, it needs to initialize the GPIO and ADC pinout and read the ADC overlay for the configurations. From the overlay, the hardware will perform the ADC measurement based on the settings. After initialized complete, the ADC measurement will be inserted to a thread.
After successfully initialize the ADC peripheral at one run, the ADC measurement of Soil Sensor will be inserted to a Thread. In this thread, the measurement of ADC value will be performed periodically. ADC value in the memory buffer need to be converted into it's literal value and the stored into Cluster buffer.
On the linux PC side, by sending chiptool command to read data Cluster from Matter device, the linux PC will receive the feedback data from Matter device in raw. Means that we need to filter out the unwanted data from the raw package data. This can be done by simple bash script. This script will simply inject chiptool command to the terminal, waiting for data return, and the Dump the data into a dump file. After the dump process is finish, the script will perform data parse and do filtering/decisioning of the soil status based on the sensor value. Whether its dry, normal or excess water.
CodeThe code in this project is inherited from Matter Light Bulb example. This way will able to shorten the development process time. It can directly use the Endpoint, Cluster and function inside the Matter cluster without worry about any complicated integration. It will also allow us to step forward into application implementation, which is mean it more efficient in time and effort.
Additional option that need to be enabled is ADC at Device Drivers. This option will enable the ADC on the board NRF7002-DK.
ADC Overlay.
Overlay is where it passing the ADC settings, shown in code below.
/*
USER OVERLAY
*/
/ {
zephyr,user {
io-channels = <&adc 0>, <&adc 1>, <&adc 2>, <&adc 3>, <&adc 4>, <&adc 5>, <&adc 6>, <&adc 7>;
};
};
&adc {
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>; ### Reg number
zephyr,gain = "ADC_GAIN_1_6"; ### GAIN
zephyr,reference = "ADC_REF_INTERNAL"; ### Voltage reference
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>; ### Timer sampling
zephyr,input-positive = <NRF_SAADC_AIN0>; ### Single input
zephyr,resolution = <12>; ### ADC Resolution
};
.
.
.
.
.
.
channel@7 {
reg = <7>;
zephyr,gain = "ADC_GAIN_1_6";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,input-positive = <NRF_SAADC_VDD>;
zephyr,resolution = <12>;
zephyr,oversampling = <8>;
};
};
ADC overlay is needed to pass the ADC configuration to ADC library. In this Overlay with tagname zephyr, user, this overlay contains 8 IO-Channel (based on NRF7002-DK dedicated ADC inputs). The Channel 0, where the soil sensor will connected into, has gain "ADC_GAIN_1_6", voltage reference "ADC_REF_INTERNAL", with ADC sampling time <ADC_ACQ_TIME_DEFAULT>. And we want to choose single input instead differential input <NRF_SAADC_AIN0> with ADC resolution 12 bit.
ADC application
It will create new AdcApp class to accomodate Sensor measurement. This includes 3 main functions, those are AdcTest() to check the each ADC functional pins, SoilSensorInit to initialize ADC pin for specific IO, and ReadSoilSensor to read sensor soil measurement value.
class AdcApp {
public:
void AdcTest();
void SoilSensorInit(uint8_t channel);
uint8_t ReadSoilSensor(uint8_t channel);
};
ADC Initialization
In this Initialization code, it calls the overlay that already stated in Overlay file with tag name zephyr_user. Then if the detection is succeess, it create ADC sequence.
/*
CUSTOM OVERLAY POINTER
*/
// Check if there is tag naming zephyr_user
#if !DT_NODE_EXISTS(DT_PATH(zephyr_user)) || \
!DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels)
#error "No suitable devicetree overlay specified"
#endif
#define DT_SPEC_AND_COMMA(node_id, prop, idx) \
ADC_DT_SPEC_GET_BY_IDX(node_id, idx),
uint16_t buf;
/* Data of ADC io-channels specified in devicetree. */
static const struct adc_dt_spec adc_channels[] = {
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels,
DT_SPEC_AND_COMMA)};
// Create ADC sequence
// safe data to buffer
struct adc_sequence sequence = {
.buffer = &buf,
/* buffer size in bytes, not number of samples */
.buffer_size = sizeof(buf),
};
ADC Test
ADC test is the part of ADC app class. this will perform ADC functional check and conversion depend on the ADC settings. This check is simply performing ADC initialization for each ADC channels starting channel number 0 to 7, and continued to measure and perform conversion to digital.
void AdcApp::AdcTest()
{
int err;
uint32_t count = 0;
/* Configure channels individually prior to sampling. */
for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) {
if (!adc_is_ready_dt(&adc_channels[i])) {
printk("ADC controller device %s not ready\n", adc_channels[i].dev->name);
}
err = adc_channel_setup_dt(&adc_channels[i]);
if (err < 0) {
printk("Could not setup channel #%d (%d)\n", i, err);
}
}
printk("ADC reading[%u]:\n", count++);
for (size_t i = 0U; i < ARRAY_SIZE(adc_channels); i++) {
int32_t val_mv;
printk("- %s, channel %d: ", adc_channels[i].dev->name, adc_channels[i].channel_id);
(void)adc_sequence_init_dt(&adc_channels[i], &sequence);
err = adc_read(adc_channels[i].dev, &sequence);
if (err < 0) {
printk("Could not read (%d)\n", err);
}
val_mv = (int32_t)buf;
printk("%"PRId32, val_mv);
err = adc_raw_to_millivolts_dt(&adc_channels[i], &val_mv);
/* conversion to mV may not be supported, skip if not */
if (err < 0) {
printk(" (value in mV not available)\n");
} else {
printk(" = %"PRId32" mV\n", val_mv);
}
}
k_sleep(K_MSEC(1000));
}
SoilSensor Initialize.
This code is used to initialize specific ADC Input pin, which is used for SoilSensor. First it will check whether the adc channel is ready, and then it will set up the adc channel properly. After that the code will create the sequence for particular adc input.
// Initialize sensor
void AdcApp::SoilSensorInit(uint8_t channel)
{
int err;
printk("Initialize ADC Soil sensor\n");
if (!adc_is_ready_dt(&adc_channels[channel])) {
printk("ADC controller device %s not ready\n", adc_channels[channel].dev->name);
}
err = adc_channel_setup_dt(&adc_channels[channel]);
if (err < 0) {
printk("Could not setup channel #%d (%d)\n", channel, err);
}
(void)adc_sequence_init_dt(&adc_channels[channel], &sequence);
k_sleep(K_MSEC(100));
}
Read Sensor Data
This is the function to read SoilSensor measurement value, this function later will be called inside a thread to perform periodically measurement. First it started from creating variables for sensor data. Continued by reading of ADC input data, in this project, the input is channel 0. the function adc_read will sampling and taking in ADC measurement value, and then with adc-raw_to_milivolt_dt will convert it to milivolt. later then the mV will be converted to scale measurement.
Conversion of scale measurement is created based on experiment. First trial when sensor is hanging in open air, the adc value is around 2000mV. and when soil sensor is buried on the soil, it showing under 1800mV. So the limit whether the soil is dry and humid is at 1800mV. This is why this code is taking 1800mV as the limit, any measurement above 1800mV will be categorized as Dry condition.
// Read sensor data
uint8_t AdcApp::ReadSoilSensor(uint8_t channel)
{
uint8_t sensor_val;
int32_t val_mv;
float temp_val_mv;
int err;
printk("Read Soil Sensor Value\n");
// Get ADC Data channel 0
err = adc_read(adc_channels[channel].dev, &sequence);
if (err < 0) {
printk("Could not read (%d)\n", err);
}
val_mv = (int32_t)buf;
err = adc_raw_to_millivolts_dt(&adc_channels[channel], &val_mv);
/* conversion to mV may not be supported, skip if not */
if (err < 0) {
printk(" (value in mV not available)\n");
} else {
printk(" ADC Val = %"PRId32" mV\n", val_mv);
}
// convert
temp_val_mv = (float(val_mv)/1807) ;
sensor_val = uint8_t (temp_val_mv * 100);
printk("float temp_val (%f)\n", temp_val_mv);
printk("sensor_val (%d)\n", sensor_val);
k_sleep(K_MSEC(100));
return sensor_val;
}
Thread function
This new Thread will be called in main function, it will run the ReadSoilSensor function inside AdcApp every 10 seconds. After it successfully read the sensor value, the code will save the data inside cluster buffer. later then the data inside cluster will be requested by Chiptool.
void AppTask::SoilSensorReadApp(void *, void *, void *)
{
while (1){
SoilSensorVal = Adcapp.ReadSoilSensor(0);
LOG_INF("Soil sensor val: %d \n", SoilSensorVal);
/* Custom: write ADC/Soilmeasurement */
EmberAfStatus status = Clusters::LevelControl::Attributes::CurrentLevel::Set(kLightEndpointId, SoilSensorVal);
if (status != EMBER_ZCL_STATUS_SUCCESS) {
LOG_ERR("Updating level cluster failed: %x", status);
}
k_sleep(K_MSEC(10000));
}
}
Main
The main function where we call the thread and adc_app that has been created in other module. In this function it call the class, and then call the main trhread to sampling Soil Sensor data.
/*
USER DEFINE
*/
#include "adc_app.h"
/*
*/
.
.
.
/*USER CUSTOM*/
#define SENSING_STACK_SIZE 1024
#define SENSING_PRIORITY 5
K_THREAD_STACK_DEFINE(my_stack_area, SENSING_STACK_SIZE);
struct k_thread my_thread_data;
uint8_t SoilSensorVal;
/**/
.
..
.
// --- RUN THE THREAD -- //
// run thread soil sensor
k_tid_t my_tid = k_thread_create(&my_thread_data, my_stack_area,
K_THREAD_STACK_SIZEOF(my_stack_area),
SoilSensorReadApp,
NULL, NULL, NULL,
SENSING_PRIORITY, 0, K_NO_WAIT);
Bash script
The bash script is run on the linux PC. It will take in the all raw return data from the Matter Device. It will dump the data into file and parse the sensor data from raw dat. it also will tell the condition of the soil whether it is dry, normal or excess water.
#!/bin/bash
echo soil sensor value
> dump.txt
./chip-tool-debug levelcontrol read current-level 1 1 >> dump.txt
SensorVal="$(grep -oP "(?<=Data = ).*(?=,)" dump.txt)"
MaxVal=100
MinVal=40
if [[($SensorVal -gt $MaxVal)]]
then
echo "Please water your plant"
elif [[($SensorVal -lt $MaxVal) && ($SensorVal -gt $MinVal)]]
then
echo "Soil water is normal"
elif [[($SensorVal -lt $MinVal)]]
then
echo "too much water"
fi
echo "$SensorVal"
Rebuild the ProjectTo be able to rebuild the project, required steps as follows:
1. Prepare any plants as test object
2. Make sure required Software and Hardware are installed properly. For VScode NRF compatible library, please download NRF VSCode extentension.
3. Download the complete code from Github. Put the firmware code inside VSCode project folder and start compiling
4. Download the Install properly the Chiptool for Matter device
5. Start Provisioning Matter Device using Chiptool
6. Run Bash script to read sensor soil data inside Matter device
7. Put bash script on linux auto run scripts
ResultProvisioning
Pairing the Matter Device with Chiptool required provisioning. In this project, provisioning is done through WIFI and BLE, make sure both BLE and WIFI 6 AP is available, otherwise it will fail. Through the provisioning, Chiptool will try to detect Matter Device and requested for pairing. If the pairing is successful, Chiptool will show it pairing status as success.
ADC Test
From the code for soil sensor measurement test, it will showing the raw value in mV and conversion value.
Soil Sensor data read
This is the result of home garden monitoring using Matter Device. The measurement was held to simulate dry condition and normal condition. Dry condition is simulated by detach sensor from the soil, and the start query the sensor data from matter device. And then sensor is buried inside the soil to check the soil condition.
Result are shown in the terminal by run the bash script command./home_garden_check.sh, and the result will show the soil condition with measurement value.
1. Robustness: The provisioning have issue in robustness. It needs multiple trial to get Matter Device paired with chiptool. Also the device might need to re-paired after power off, or after hardware reset. When the Matter Device saved the pairing result, somehow it can't be detected from Chiptool. To solve this proble, it need to re-flashing the NRF7002-DK
2. Compatibility: The Matter Device is not compatible with Google Home. After the software trying to pair with NRF7002-DK, Seems that the Board never accepting the pairing request.
Comments