diff --git a/.github/actions/build/build.sh b/.github/actions/build/build.sh index 5a8f8fc..b32fc87 100755 --- a/.github/actions/build/build.sh +++ b/.github/actions/build/build.sh @@ -7,8 +7,15 @@ REVISION=$3 CMAKE_EXTRA=$4 OUTPUT_BIN=$5 +TODAY=$(date +'%Y-%m-%d') + +# Replaces occurrences of "__TODAY__" with $TODAY in $CMAKE_EXTRA. +CMAKE_EXTRA="${CMAKE_EXTRA/__TODAY__/"$TODAY"}" + cd "${GITHUB_WORKSPACE}/${SAMPLE_DIR}" +echo $CMAKE_EXTRA + west build --build-dir ./build --pristine --board "${BOARD}@${REVISION}" -- $CMAKE_EXTRA mv build/zephyr/zephyr.hex build/zephyr/"${OUTPUT_BIN}" \ No newline at end of file diff --git a/.github/workflows/b-parasite.yml b/.github/workflows/b-parasite.yml index a4cac62..c577bba 100644 --- a/.github/workflows/b-parasite.yml +++ b/.github/workflows/b-parasite.yml @@ -17,6 +17,7 @@ jobs: uses: jidicula/clang-format-action@v4.9.0 with: check-path: 'code/nrf-connect' + exclude-regex: '\/build\/' - name: Build blinky uses: ./.github/actions/build with: @@ -71,4 +72,11 @@ jobs: with: sample-dir: code/nrf-connect/samples/ble board: bparasite_nrf52833 - output-bin: ble_nrf52833_default.hex \ No newline at end of file + output-bin: ble_nrf52833_default.hex + - name: Build zigbee_nrf528340_default.hex + uses: ./.github/actions/build_and_upload + with: + sample-dir: code/nrf-connect/samples/zigbee + board: bparasite_nrf52840 + output-bin: zigbee_nrf52840_default.hex + cmake-extra: -DPRST_ZB_BUILD_DATE=__TODAY__ diff --git a/README.md b/README.md index 71c1a20..28c0d1e 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ This repository also hosts a few different firmware samples for b-parasite. |Sample|Description|Extra Documentation| |---|---|---| |[samples/ble](./code/nrf-connect/samples/ble)|This is the most battle-tested and useful firmware. It periodically reads all sensors and broadcast them via Bluetooth Low Energy (BLE). It works with [Home Assistant](https://www.home-assistant.io/) out of the box. |[Docs](./code/nrf-connect/samples/ble/README.md)| +|[samples/zigbee](./code/nrf-connect/samples/zigbee)| An experimental/educational/exploratory basic Zigbee sample built on [nRF Connect + ZBOSS](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/ug_zigbee.html). It integrates with [Home Assistant](https://www.home-assistant.io/) via [ZHA](https://www.home-assistant.io/integrations/zha) or [Zigbee2MQTT](https://www.zigbee2mqtt.io/). |[Docs](./code/nrf-connect/samples/zigbee/README.md)| |[samples/blinky](./code/nrf-connect/samples/blinky)| The classic "Hello, world" |-| |[samples/soil_read_loop](./code/nrf-connect/samples/soil_read_loop)| Read the soil moisture sensor on a loop. Useful for experimenting and calibrating the sensor. |-| diff --git a/code/nrf-connect/prstlib/Kconfig b/code/nrf-connect/prstlib/Kconfig new file mode 100644 index 0000000..23a7071 --- /dev/null +++ b/code/nrf-connect/prstlib/Kconfig @@ -0,0 +1,3 @@ +module = PRSTLIB +module-str = prstlib +source "subsys/logging/Kconfig.template.log_config" diff --git a/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52833/Kconfig b/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52833/Kconfig index b6f2e0e..a0c83d3 100644 --- a/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52833/Kconfig +++ b/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52833/Kconfig @@ -6,4 +6,4 @@ config BOARD_ENABLE_DCDC select SOC_DCDC_NRF52X default y -endif # BOARD_BPARASITE_NRF52833 \ No newline at end of file +endif # BOARD_BPARASITE_NRF52833 diff --git a/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52840/Kconfig b/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52840/Kconfig index aa0eeee..4ce925f 100644 --- a/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52840/Kconfig +++ b/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52840/Kconfig @@ -11,4 +11,4 @@ config BOARD_ENABLE_DCDC_HV select SOC_DCDC_NRF52X_HV default y -endif # BOARD_BPARASITE_NRF52840 \ No newline at end of file +endif # BOARD_BPARASITE_NRF52840 diff --git a/code/nrf-connect/prstlib/include/prstlib/adc.h b/code/nrf-connect/prstlib/include/prstlib/adc.h index 5ec3e9e..2fc29ce 100644 --- a/code/nrf-connect/prstlib/include/prstlib/adc.h +++ b/code/nrf-connect/prstlib/include/prstlib/adc.h @@ -9,6 +9,11 @@ typedef struct { float voltage; } prst_adc_read_t; +typedef struct { + prst_adc_read_t adc_read; + float percentage; +} prst_batt_t; + typedef struct { prst_adc_read_t adc_read; // A value from 0 (completely dry) to 2^10 (completely wet). @@ -25,7 +30,7 @@ typedef struct prst_adc_photo_sensor { int prst_adc_init(); -int prst_adc_batt_read(prst_adc_read_t* out); +int prst_adc_batt_read(prst_batt_t* out); int prst_adc_soil_read(float battery_voltage, prst_adc_soil_moisture_t* out); diff --git a/code/nrf-connect/prstlib/include/prstlib/sensors.h b/code/nrf-connect/prstlib/include/prstlib/sensors.h index 23c1c03..0f8f669 100644 --- a/code/nrf-connect/prstlib/include/prstlib/sensors.h +++ b/code/nrf-connect/prstlib/include/prstlib/sensors.h @@ -7,7 +7,7 @@ typedef struct { prst_adc_soil_moisture_t soil; prst_adc_photo_sensor_t photo; - prst_adc_read_t batt; + prst_batt_t batt; prst_shtc3_read_t shtc3; } prst_sensors_t; diff --git a/code/nrf-connect/prstlib/src/adc.c b/code/nrf-connect/prstlib/src/adc.c index d22f542..f13232d 100644 --- a/code/nrf-connect/prstlib/src/adc.c +++ b/code/nrf-connect/prstlib/src/adc.c @@ -9,7 +9,7 @@ #include "prstlib/macros.h" -LOG_MODULE_REGISTER(adc, LOG_LEVEL_WRN); +LOG_MODULE_REGISTER(adc, CONFIG_PRSTLIB_LOG_LEVEL); // PWM spec for square wave. Input to the soil sensing circuit. static const struct pwm_dt_spec soil_pwm_dt = @@ -48,6 +48,37 @@ struct gpio_dt_spec ldr_enable_dt = #endif +typedef struct { + // High (h) and low (p) voltage (v) and % (p) points. + float vh, vl, ph, pl; +} batt_disch_linear_section_t; + +static void set_battery_percent(const prst_adc_read_t* read, prst_batt_t* out) { + // Must be sorted by .vh. + static const batt_disch_linear_section_t sections[] = { + {.vh = 3.00f, .vl = 2.90f, .ph = 1.00f, .pl = 0.42f}, + {.vh = 2.90f, .vl = 2.74f, .ph = 0.42f, .pl = 0.18f}, + {.vh = 2.74f, .vl = 2.44f, .ph = 0.18f, .pl = 0.06f}, + {.vh = 2.44f, .vl = 2.01f, .ph = 0.06f, .pl = 0.00f}, + }; + + const float v = read->voltage; + + if (v > sections[0].vh) { + out->percentage = 1.0f; + return; + } + for (int i = 0; i < ARRAY_SIZE(sections); i++) { + const batt_disch_linear_section_t* s = §ions[i]; + if (v > s->vl) { + out->percentage = s->pl + (v - s->vl) * ((s->ph - s->pl) / (s->vh - s->vl)); + return; + } + } + out->percentage = 0.0f; + return; +} + static inline float get_soil_moisture_percent(float battery_voltage, int16_t raw_adc_output) { const double x = battery_voltage; @@ -93,8 +124,9 @@ int prst_adc_init() { return 0; } -int prst_adc_batt_read(prst_adc_read_t* out) { - RET_IF_ERR(read_adc_spec(&adc_batt_spec, out)); +int prst_adc_batt_read(prst_batt_t* out) { + RET_IF_ERR(read_adc_spec(&adc_batt_spec, &out->adc_read)); + set_battery_percent(&out->adc_read, out); return 0; } diff --git a/code/nrf-connect/prstlib/src/button.c b/code/nrf-connect/prstlib/src/button.c index a27eda2..54f2f2a 100644 --- a/code/nrf-connect/prstlib/src/button.c +++ b/code/nrf-connect/prstlib/src/button.c @@ -6,7 +6,7 @@ #include "prstlib/led.h" #include "prstlib/macros.h" -LOG_MODULE_REGISTER(button, LOG_LEVEL_WRN); +LOG_MODULE_REGISTER(button, CONFIG_PRSTLIB_LOG_LEVEL); static struct gpio_dt_spec button = GPIO_DT_SPEC_GET(DT_NODELABEL(button0), gpios); diff --git a/code/nrf-connect/prstlib/src/led.c b/code/nrf-connect/prstlib/src/led.c index 415296f..9ba4d47 100644 --- a/code/nrf-connect/prstlib/src/led.c +++ b/code/nrf-connect/prstlib/src/led.c @@ -4,7 +4,7 @@ #include "prstlib/macros.h" -LOG_MODULE_REGISTER(led, LOG_LEVEL_WRN); +LOG_MODULE_REGISTER(led, CONFIG_PRSTLIB_LOG_LEVEL); struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_NODELABEL(led0), gpios); diff --git a/code/nrf-connect/prstlib/src/sensors.c b/code/nrf-connect/prstlib/src/sensors.c index 8b8cc2a..b826d1c 100644 --- a/code/nrf-connect/prstlib/src/sensors.c +++ b/code/nrf-connect/prstlib/src/sensors.c @@ -6,21 +6,22 @@ #include "prstlib/led.h" #include "prstlib/macros.h" -LOG_MODULE_REGISTER(sensors, LOG_LEVEL_WRN); +LOG_MODULE_REGISTER(sensors, CONFIG_PRSTLIB_LOG_LEVEL); int prst_sensors_read_all(prst_sensors_t *sensors) { RET_IF_ERR(prst_adc_batt_read(&sensors->batt)); - RET_IF_ERR(prst_adc_soil_read(sensors->batt.voltage, &sensors->soil)); - RET_IF_ERR(prst_adc_photo_read(sensors->batt.voltage, &sensors->photo)); + RET_IF_ERR(prst_adc_soil_read(sensors->batt.adc_read.voltage, &sensors->soil)); + RET_IF_ERR(prst_adc_photo_read(sensors->batt.adc_read.voltage, &sensors->photo)); RET_IF_ERR(prst_shtc3_read(&sensors->shtc3)) - LOG_DBG("Batt: %d mV", sensors->batt.millivolts); - LOG_DBG("Soil: %.0f %% (%.3f mV)", 100 * sensors->soil.percentage, - sensors->soil.adc_read.voltage); - LOG_DBG("Photo: %u lx (%.3f mV)", sensors->photo.brightness, - sensors->soil.adc_read.voltage); + LOG_DBG("Batt: %d mV (%.2f%%)", sensors->batt.adc_read.millivolts, + 100 * sensors->batt.percentage); + LOG_DBG("Soil: %.0f %% (%d mV)", 100 * sensors->soil.percentage, + sensors->soil.adc_read.millivolts); + LOG_DBG("Photo: %u lx (%d mV)", sensors->photo.brightness, + sensors->soil.adc_read.millivolts); LOG_DBG("Temp: %f oC", sensors->shtc3.temp_c); - LOG_DBG("Humi: %.0f %%", 100.0 * sensors->shtc3.rel_humi); + LOG_DBG("Humi: %.0f %%", 100 * sensors->shtc3.rel_humi); LOG_DBG("--------------------------------------------------"); return 0; diff --git a/code/nrf-connect/prstlib/src/shtc3.c b/code/nrf-connect/prstlib/src/shtc3.c index 90cd6ba..189a221 100644 --- a/code/nrf-connect/prstlib/src/shtc3.c +++ b/code/nrf-connect/prstlib/src/shtc3.c @@ -6,7 +6,7 @@ #include "prstlib/macros.h" -LOG_MODULE_REGISTER(shtc3, LOG_LEVEL_WRN); +LOG_MODULE_REGISTER(shtc3, CONFIG_PRSTLIB_LOG_LEVEL); static const struct i2c_dt_spec shtc3 = I2C_DT_SPEC_GET(DT_NODELABEL(shtc3)); diff --git a/code/nrf-connect/samples/ble/Kconfig b/code/nrf-connect/samples/ble/Kconfig index 6c505c1..d2d47c5 100644 --- a/code/nrf-connect/samples/ble/Kconfig +++ b/code/nrf-connect/samples/ble/Kconfig @@ -1,5 +1,6 @@ source "Kconfig.zephyr" +rsource "../../prstlib/Kconfig" config PRST_SLEEP_DURATION_SEC int "Sleep duration in seconds" diff --git a/code/nrf-connect/samples/ble/src/encoding.c b/code/nrf-connect/samples/ble/src/encoding.c index 82a5a21..93b24d1 100644 --- a/code/nrf-connect/samples/ble/src/encoding.c +++ b/code/nrf-connect/samples/ble/src/encoding.c @@ -25,8 +25,8 @@ int prst_ble_encode_service_data(const prst_sensors_t* sensors, // 4 bits for a small wrap-around counter for deduplicating messages on the // receiver. // out[3] = sensors->run_counter & 0x0f; - out[4] = sensors->batt.millivolts >> 8; - out[5] = sensors->batt.millivolts & 0xff; + out[4] = sensors->batt.adc_read.millivolts >> 8; + out[5] = sensors->batt.adc_read.millivolts & 0xff; int16_t temp_centicelsius = 100 * sensors->shtc3.temp_c; out[6] = temp_centicelsius >> 8; out[7] = temp_centicelsius & 0xff; @@ -82,7 +82,7 @@ int prst_ble_encode_service_data(const prst_sensors_t* sensors, // Type - voltage. out[15] = 0x0c; // Value. Factor of 0.001. - uint16_t batt_val = sensors->batt.millivolts; + uint16_t batt_val = sensors->batt.adc_read.millivolts; out[16] = batt_val & 0xff; out[17] = batt_val >> 8; @@ -116,7 +116,7 @@ int prst_ble_encode_service_data(const prst_sensors_t* sensors, // Battery voltage. out[13] = 0x0c; // Value. Factor of 0.001. - uint16_t batt_val = sensors->batt.millivolts; + uint16_t batt_val = sensors->batt.adc_read.millivolts; out[14] = batt_val & 0xff; out[15] = batt_val >> 8; // Soil moisture. diff --git a/code/nrf-connect/samples/soil_read_loop/Kconfig b/code/nrf-connect/samples/soil_read_loop/Kconfig new file mode 100644 index 0000000..8f070f8 --- /dev/null +++ b/code/nrf-connect/samples/soil_read_loop/Kconfig @@ -0,0 +1,2 @@ +source "Kconfig.zephyr" +rsource "../../prstlib/Kconfig" diff --git a/code/nrf-connect/samples/zigbee/.gitignore b/code/nrf-connect/samples/zigbee/.gitignore new file mode 100644 index 0000000..2286ab6 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/.gitignore @@ -0,0 +1,2 @@ +build +build_* diff --git a/code/nrf-connect/samples/zigbee/CMakeLists.txt b/code/nrf-connect/samples/zigbee/CMakeLists.txt new file mode 100644 index 0000000..db0b194 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.20.0) + +# Pull in the dts/ and boards/ from prstlib. +set(DTS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../prstlib) +set(BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../prstlib) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project("b-parasite Zigbee sample") + +include_directories(src) + +target_sources(app PRIVATE + src/main.c + src/prst_zb_attrs.c + src/prst_zb_soil_moisture_defs.c +) + +add_subdirectory(../../prstlib prstlib) +target_include_directories(app PRIVATE ../../prstlib/include) +target_link_libraries(app PUBLIC prstlib) \ No newline at end of file diff --git a/code/nrf-connect/samples/zigbee/Kconfig b/code/nrf-connect/samples/zigbee/Kconfig new file mode 100644 index 0000000..39b9e88 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/Kconfig @@ -0,0 +1,22 @@ +source "Kconfig.zephyr" +rsource "../../prstlib/Kconfig" + +config PRST_ZB_SLEEP_DURATION_SEC + int "Sleep duration between waking up and reading sensors in seconds." + default 600 + +config PRST_ZB_PARENT_POLL_INTERVAL_SEC + int "Interval for when b-parasite polls its parent for data in seconds." + default 60 + +config PRST_ZB_BUILD_DATE + string "Zigbee basic cluster build date attribute. Max 16 bytes." + default "" + +config PRST_ZB_MODEL_ID + string "Zigbee basic cluster model id attribute. Max 32 bytes." + default "b-parasite" + +config PRST_ZB_HARDWARE_VERSION + int "Zigbee basic cluster hardware version attribute. 1 byte." + default 2 diff --git a/code/nrf-connect/samples/zigbee/README.md b/code/nrf-connect/samples/zigbee/README.md new file mode 100644 index 0000000..39504d3 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/README.md @@ -0,0 +1,46 @@ +# Zigbee firmware sample +This sample is adapted from the [zigbee_template](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/samples/zigbee/template/README.html) from the nRF Connect SDK. It's a basic experimental/educational/exploratory firmware sample for b-parasite. + +## Clusters +These [clusters](https://en.wikipedia.org/wiki/Zigbee#Cluster_library) are defined in the sample: + +|Cluster ID|Name| +|--------|---| +|0x0001|Power Configuration| +|0x0400|Illuminance Measurement| +|0x0402|Temperature Measurement| +|0x0405|Relative Humidity Measurement| +|0x0408|Soil Moisture Measurement| + +## Pairing Mode +The sample will first boot and start looking for a Zigbee coordinator - in pairing mode. The onboard LED will be flashing once a second while in this mode. Once a suitable network is found, the LED will briefly flash 3 times and remain off. + +### Factory Reset +Most Zigbee devices provide a physical button to "factory reset" it - causing it to forget its joined network and look for a new one. + +b-parasite has no physical buttons, and the implemented work around is to distinguish between two *reset modes*: +#### Power up mode +The device enters this mode when it is powered. For example, swapping an old battery or connecting to eternal power. This is the "usual" reset mode, and joined networks will be remembered. + +#### Reset pin mode +If the device's RESET pin is briefly grounded, the device will effectively be **factory reset**. The device will leave its previous network and start looking for a new one. + +## Configs +Available options in `Kconfig`. Notable options: +* `CONFIG_PRST_ZB_SLEEP_DURATION_SEC`: amount of time (in seconds) the device sleeps between reading all sensors and updating its clusters +* `CONFIG_PRST_ZB_PARENT_POLL_INTERVAL_SEC`: amount of time (in seconds) the device waits between polling its parent for data + +## Home Assistant Integration +This firmware sample has only been tested with Home Assistant, using one of the following integrations. + +### Zigbee Home Automation (ZHA) +With the [ZHA](https://www.home-assistant.io/integrations/zha) Home Assistant integration, b-parasite should work out of the box. + +### Zigbee2MQTT & Home Assistant +With [Zigbee2MQTT](https://zigbee2mqtt.io/), a custom converter is required. The [b-parasite.js](b-parasite.js) file contains such a converter. See [Support new devices](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html) for instructions. + +## Battery Life +While sleeping, the device consumes around 2 uA: +![sleeping current](./media/power-profile/sleeping.png) +In the active cycle, it averages around 125 uA for 1 second: +![active current](media/power-profile/active.png) diff --git a/code/nrf-connect/samples/zigbee/b-parasite.js b/code/nrf-connect/samples/zigbee/b-parasite.js new file mode 100644 index 0000000..63654ff --- /dev/null +++ b/code/nrf-connect/samples/zigbee/b-parasite.js @@ -0,0 +1,47 @@ +// This is a zigbee2mqtt zigbee2mqtt.io converter for this sample. +// See https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html +// on how to use it. + +const fz = require('zigbee-herdsman-converters/converters/fromZigbee'); +const tz = require('zigbee-herdsman-converters/converters/toZigbee'); +const exposes = require('zigbee-herdsman-converters/lib/exposes'); +const reporting = require('zigbee-herdsman-converters/lib/reporting'); +const extend = require('zigbee-herdsman-converters/lib/extend'); +const e = exposes.presets; +const ea = exposes.access; + +const definition = { + zigbeeModel: ['b-parasite'], + model: 'b-parasite', + vendor: 'b-parasite', + description: 'IoT development board - Zigbee sample', + fromZigbee: [fz.temperature, fz.humidity, fz.battery, fz.soil_moisture, fz.illuminance], + toZigbee: [], + exposes: [ + e.temperature(), + e.humidity(), + e.battery(), + e.soil_moisture(), + e.illuminance_lux()], + configure: async (device, coordinatorEndpoint, logger) => { + const endpoint = device.getEndpoint(10); + await reporting.bind( + endpoint, + coordinatorEndpoint, [ + 'genPowerCfg', + 'msTemperatureMeasurement', + 'msRelativeHumidity', + 'msSoilMoisture', + 'msIlluminanceMeasurement', + ]); + await reporting.batteryPercentageRemaining(endpoint); + // Not reportable :( + // await reporting.batteryVoltage(endpoint); + await reporting.temperature(endpoint); + await reporting.humidity(endpoint); + await reporting.soil_moisture(endpoint); + await reporting.illuminance(endpoint); + } +}; + +module.exports = definition; \ No newline at end of file diff --git a/code/nrf-connect/samples/zigbee/media/power-profile/active.png b/code/nrf-connect/samples/zigbee/media/power-profile/active.png new file mode 100644 index 0000000..b09cdc9 Binary files /dev/null and b/code/nrf-connect/samples/zigbee/media/power-profile/active.png differ diff --git a/code/nrf-connect/samples/zigbee/media/power-profile/sleeping.png b/code/nrf-connect/samples/zigbee/media/power-profile/sleeping.png new file mode 100644 index 0000000..e0f5a11 Binary files /dev/null and b/code/nrf-connect/samples/zigbee/media/power-profile/sleeping.png differ diff --git a/code/nrf-connect/samples/zigbee/prj.conf b/code/nrf-connect/samples/zigbee/prj.conf new file mode 100644 index 0000000..4a91f5d --- /dev/null +++ b/code/nrf-connect/samples/zigbee/prj.conf @@ -0,0 +1,43 @@ + +CONFIG_LOG=y +CONFIG_PWM=y +CONFIG_I2C=y +CONFIG_ADC=y +CONFIG_GPIO=y + +CONFIG_PM=y +CONFIG_PM_DEVICE=y + +CONFIG_NEWLIB_LIBC=y +CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y + +CONFIG_SERIAL=n + +CONFIG_HEAP_MEM_POOL_SIZE=2048 +CONFIG_MAIN_THREAD_PRIORITY=7 + +CONFIG_ZIGBEE=y +CONFIG_ZIGBEE_APP_UTILS=y +CONFIG_ZIGBEE_ROLE_END_DEVICE=y + +# This example requires more workqueue stack +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +# Enable nRF ECB driver +CONFIG_CRYPTO=y +CONFIG_CRYPTO_NRF_ECB=y +CONFIG_CRYPTO_INIT_PRIORITY=80 + +# Networking +CONFIG_NET_IPV6_MLD=n +CONFIG_NET_IPV6_NBR_CACHE=n +CONFIG_NET_IPV6_RA_RDNSS=n +CONFIG_NET_IP_ADDR_CHECK=n +CONFIG_NET_UDP=n + +# Get Zigbee to scan every channel. +CONFIG_ZIGBEE_CHANNEL_SELECTION_MODE_MULTI=y + +# Enable API for powering down unused RAM parts. +# https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.7.1/nrf/ug_zigbee_configuring.html#power-saving-during-sleep +CONFIG_RAM_POWER_DOWN_LIBRARY=y diff --git a/code/nrf-connect/samples/zigbee/prj_debug.conf b/code/nrf-connect/samples/zigbee/prj_debug.conf new file mode 100644 index 0000000..734b637 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/prj_debug.conf @@ -0,0 +1,49 @@ + +CONFIG_LOG=y +CONFIG_PWM=y +CONFIG_I2C=y +CONFIG_ADC=y +CONFIG_GPIO=y + +CONFIG_PM=y +CONFIG_PM_DEVICE=y + +CONFIG_NEWLIB_LIBC=y +CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y + +CONFIG_SERIAL=n + +CONFIG_HEAP_MEM_POOL_SIZE=2048 +CONFIG_MAIN_THREAD_PRIORITY=7 + +CONFIG_ZIGBEE=y +CONFIG_ZIGBEE_APP_UTILS=y +CONFIG_ZIGBEE_ROLE_END_DEVICE=y + +# This example requires more workqueue stack +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +# Enable nRF ECB driver +CONFIG_CRYPTO=y +CONFIG_CRYPTO_NRF_ECB=y +CONFIG_CRYPTO_INIT_PRIORITY=80 + +# Networking +CONFIG_NET_IPV6_MLD=n +CONFIG_NET_IPV6_NBR_CACHE=n +CONFIG_NET_IPV6_RA_RDNSS=n +CONFIG_NET_IP_ADDR_CHECK=n +CONFIG_NET_UDP=n + +# Get Zigbee to scan every channel. +CONFIG_ZIGBEE_CHANNEL_SELECTION_MODE_MULTI=y + +# Enable API for powering down unused RAM parts. +# https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.7.1/nrf/ug_zigbee_configuring.html#power-saving-during-sleep +CONFIG_RAM_POWER_DOWN_LIBRARY=y + +# Config options in Kconfig. +CONFIG_PRST_ZB_SLEEP_DURATION_SEC=10 + +CONFIG_LOG_DEFAULT_LEVEL=4 +CONFIG_PRSTLIB_LOG_LEVEL_DBG=y diff --git a/code/nrf-connect/samples/zigbee/src/main.c b/code/nrf-connect/samples/zigbee/src/main.c new file mode 100644 index 0000000..522acce --- /dev/null +++ b/code/nrf-connect/samples/zigbee/src/main.c @@ -0,0 +1,232 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prst_zb_attrs.h" +#include "prst_zb_endpoint_defs.h" +#include "prst_zb_soil_moisture_defs.h" + +#define FACTORY_RESET_BUTTON DK_BTN4_MSK + +LOG_MODULE_REGISTER(app, CONFIG_LOG_DEFAULT_LEVEL); + +static struct zb_device_ctx dev_ctx; + +static prst_sensors_t sensors; + +static void maybe_erase_pairing_info(struct k_timer *timer) { + uint32_t reset_reason = nrf_power_resetreas_get(NRF_POWER); + // If we're resetting via the RESET pin (e.g.: reset pin shorting, firmware flashing). + if (reset_reason & 0x1) { + LOG_WRN("Manual reset / re-flashing detected - erasing pairing info"); + // TODO: consider zb_bdb_reset_via_local_action(/*param=*/0); + zigbee_erase_persistent_storage(/*erase=*/true); + } else { // It's a power-on cycle (e.g.: swapping battery, first boot). + LOG_INF("Power-on cycle - keeping pairing info"); + } +} + +static void led_flashing_cb(struct k_timer *timer) { + prst_led_toggle(); +} + +K_TIMER_DEFINE(led_flashing_timer, led_flashing_cb, /*stop_fn=*/NULL); + +ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST( + identify_attr_list, + &dev_ctx.identify_attr.identify_time); + +ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT( + basic_attr_list, + &dev_ctx.basic_attr.zcl_version, + &dev_ctx.basic_attr.app_version, + &dev_ctx.basic_attr.stack_version, + &dev_ctx.basic_attr.hw_version, + &dev_ctx.basic_attr.mf_name, + &dev_ctx.basic_attr.model_id, + &dev_ctx.basic_attr.date_code, + &dev_ctx.basic_attr.power_source, + &dev_ctx.basic_attr.location_id, + &dev_ctx.basic_attr.ph_env, + &dev_ctx.basic_attr.sw_ver); + +ZB_ZCL_DECLARE_TEMP_MEASUREMENT_ATTRIB_LIST(temp_measurement_attr_list, + &dev_ctx.temp_measure_attrs.measure_value, + &dev_ctx.temp_measure_attrs.min_measure_value, + &dev_ctx.temp_measure_attrs.max_measure_value, + &dev_ctx.temp_measure_attrs.tolerance); + +ZB_ZCL_DECLARE_REL_HUMIDITY_MEASUREMENT_ATTRIB_LIST( + rel_humi_attr_list, + &dev_ctx.rel_humidity_attrs.rel_humidity, + &dev_ctx.rel_humidity_attrs.min_val, + &dev_ctx.rel_humidity_attrs.max_val); + +// https://devzone.nordicsemi.com/f/nordic-q-a/85315/zboss-declare-power-config-attribute-list-for-battery-bat_num +#define bat_num +ZB_ZCL_DECLARE_POWER_CONFIG_BATTERY_ATTRIB_LIST_EXT( + batt_attr_list, + &dev_ctx.batt_attrs.voltage, + /*battery_size=*/ZB_ZCL_POWER_CONFIG_BATTERY_SIZE_OTHER, + /*battery_quantity=*/1, + /*battery_rated_voltage=*/NULL, + /*battery_alarm_mask=*/NULL, + /*battery_voltage_min_threshold=*/NULL, + /*battery_percentage_remaining=*/&dev_ctx.batt_attrs.percentage, + /*battery_voltage_threshold1=*/NULL, + /*battery_voltage_threshold2=*/NULL, + /*battery_voltage_threshold3=*/NULL, + /*battery_percentage_min_threshold=*/NULL, + /*battery_percentage_threshold1=*/NULL, + /*battery_percentage_threshold2=*/NULL, + /*battery_percentage_threshold3=*/NULL, + /*battery_alarm_state=*/NULL); + +PRST_ZB_ZCL_DECLARE_SOIL_MOISTURE_ATTRIB_LIST( + soil_moisture_attr_list, + &dev_ctx.soil_moisture_attrs.percentage); + +ZB_ZCL_DECLARE_ILLUMINANCE_MEASUREMENT_ATTRIB_LIST( + illuminance_attr_list, + /*value=*/&dev_ctx.illuminance_attrs.log_lux, + /*min_value=*/NULL, + /*max_value*/ NULL); + +PRST_ZB_DECLARE_CLUSTER_LIST( + app_template_clusters, + basic_attr_list, + identify_attr_list, + temp_measurement_attr_list, + rel_humi_attr_list, + batt_attr_list, + soil_moisture_attr_list, + illuminance_attr_list); + +PRST_ZB_DECLARE_ENDPOINT( + app_template_ep, + PRST_ZIGBEE_ENDPOINT, + app_template_clusters); + +ZBOSS_DECLARE_DEVICE_CTX_1_EP( + app_template_ctx, + app_template_ep); + +void zboss_signal_handler(zb_bufid_t bufid) { + // See zigbee_default_signal_handler() for all available signals. + zb_zdo_app_signal_type_t sig = zb_get_app_signal(bufid, /*sg_p=*/NULL); + zb_ret_t status = ZB_GET_APP_SIGNAL_STATUS(bufid); + switch (sig) { + case ZB_BDB_SIGNAL_STEERING: // New network. + case ZB_BDB_SIGNAL_DEVICE_REBOOT: { // Previously joined network. + LOG_DBG("Steering complete. Status: %d", status); + prst_led_flash(/*times=*/3); + if (status == RET_OK) { + k_timer_stop(&led_flashing_timer); + prst_led_off(); + } + break; + } + case ZB_ZDO_SIGNAL_SKIP_STARTUP: + case ZB_ZDO_SIGNAL_LEAVE: { + LOG_DBG("Will restart flashing"); + k_timer_start(&led_flashing_timer, K_NO_WAIT, K_SECONDS(1)); + break; + } + } + + ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid)); + if (bufid) { + zb_buf_free(bufid); + } +} + +void update_sensors_cb(zb_uint8_t arg) { + LOG_INF("Updating sensors"); + + if (prst_sensors_read_all(&sensors)) { + LOG_ERR("Unable to read sensors"); + return; + } + + // Battery voltage in units of 100 mV. + uint8_t batt_voltage = sensors.batt.adc_read.millivolts / 100; + prst_zb_set_attr_value(ZB_ZCL_CLUSTER_ID_POWER_CONFIG, + ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_VOLTAGE_ID, + &batt_voltage); + + // Battery percentage in units of 0.5%. + zb_uint8_t batt_percentage = 2 * 100 * sensors.batt.percentage; + prst_zb_set_attr_value(ZB_ZCL_CLUSTER_ID_POWER_CONFIG, + ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID, + &batt_percentage); + + // Temperature in units of 0.01 degrees Celcius. + zb_int16_t temperature_value = 100 * sensors.shtc3.temp_c; + prst_zb_set_attr_value(ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT, + ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, + &temperature_value); + + // Relative humidity in units of 0.01%. + zb_int16_t rel_humi = 100 * 100 * sensors.shtc3.rel_humi; + prst_zb_set_attr_value(ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT, + ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID, + &rel_humi); + + // Soil moisture in units of 0.01%. + zb_int16_t soil_moisture = 100 * 100 * sensors.soil.percentage; + prst_zb_set_attr_value(PRST_ZB_ZCL_ATTR_SOIL_MOISTURE_CLUSTER_ID, + PRST_ZB_ZCL_ATTR_SOIL_MOISTURE_VALUE_ID, + &soil_moisture); + + // Illuminance in 10000 * log_10(lux) + 1. + zb_int16_t log_lux = 10000 * log10((float)sensors.photo.brightness) + 1; + prst_zb_set_attr_value(ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT, + ZB_ZCL_ATTR_ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ID, + &log_lux); + + ZB_SCHEDULE_APP_ALARM(update_sensors_cb, + /*param=*/0, + ZB_TIME_ONE_SECOND * CONFIG_PRST_ZB_SLEEP_DURATION_SEC); +} + +int main(void) { + RET_IF_ERR(prst_adc_init()); + RET_IF_ERR(prst_led_init()); + RET_IF_ERR(prst_button_init()); + + maybe_erase_pairing_info(NULL); + + register_factory_reset_button(FACTORY_RESET_BUTTON); + + prst_zb_attrs_init(&dev_ctx); + + ZB_AF_REGISTER_DEVICE_CTX(&app_template_ctx); + + update_sensors_cb(/*arg=*/0); + + zb_zdo_pim_set_long_poll_interval( + ZB_TIME_ONE_SECOND * CONFIG_PRST_ZB_PARENT_POLL_INTERVAL_SEC); + power_down_unused_ram(); + + RET_IF_ERR(prst_led_flash(2)); + + zigbee_enable(); + zigbee_configure_sleepy_behavior(/*enable=*/true); + + return 0; +} diff --git a/code/nrf-connect/samples/zigbee/src/prst_zb_attrs.c b/code/nrf-connect/samples/zigbee/src/prst_zb_attrs.c new file mode 100644 index 0000000..783cd5b --- /dev/null +++ b/code/nrf-connect/samples/zigbee/src/prst_zb_attrs.c @@ -0,0 +1,38 @@ +#include "prst_zb_attrs.h" + +#include + +#include "prst_zb_endpoint_defs.h" + +void prst_zb_attrs_init(struct zb_device_ctx *dev_ctx) { + dev_ctx->basic_attr.zcl_version = ZB_ZCL_VERSION; + dev_ctx->basic_attr.power_source = ZB_ZCL_BASIC_POWER_SOURCE_BATTERY; + ZB_ZCL_SET_STRING_VAL( + dev_ctx->basic_attr.mf_name, + PRST_BASIC_MANUF_NAME, + ZB_ZCL_STRING_CONST_SIZE(PRST_BASIC_MANUF_NAME)); + + ZB_ZCL_SET_STRING_VAL( + dev_ctx->basic_attr.model_id, + CONFIG_PRST_ZB_MODEL_ID, + ZB_ZCL_STRING_CONST_SIZE(CONFIG_PRST_ZB_MODEL_ID)); + + ZB_ZCL_SET_STRING_VAL( + dev_ctx->basic_attr.date_code, + CONFIG_PRST_ZB_BUILD_DATE, + ZB_ZCL_STRING_CONST_SIZE(CONFIG_PRST_ZB_BUILD_DATE)); + + dev_ctx->basic_attr.hw_version = CONFIG_PRST_ZB_HARDWARE_VERSION; + + dev_ctx->identify_attr.identify_time = + ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE; +} + +zb_zcl_status_t prst_zb_set_attr_value(zb_uint16_t cluster_id, zb_uint16_t attr_id, void *data) { + return zb_zcl_set_attr_val(PRST_ZIGBEE_ENDPOINT, + cluster_id, + ZB_ZCL_CLUSTER_SERVER_ROLE, + attr_id, + (zb_uint8_t *)data, + ZB_FALSE); +} diff --git a/code/nrf-connect/samples/zigbee/src/prst_zb_attrs.h b/code/nrf-connect/samples/zigbee/src/prst_zb_attrs.h new file mode 100644 index 0000000..55396cd --- /dev/null +++ b/code/nrf-connect/samples/zigbee/src/prst_zb_attrs.h @@ -0,0 +1,50 @@ +#ifndef _PRST_ZB_ATTRS_H_ +#define _PRST_ZB_ATTRS_H_ + +#include + +// Relative humimdity cluster - section 4.7.2.2.1. +typedef struct { + // 100x relative humidity (mandatory). + zb_uint16_t rel_humidity; + // 0x0000 – 0x270f (mandatory). + zb_uint16_t min_val; + // 0x0001 – 0x2710 (mandatory). + zb_uint16_t max_val; +} prst_rel_humidity_attrs_t; + +// Power configuration cluster - section 3.3.2.2.3. +typedef struct { + // Units of 100 mV. 0x00 - 0xff (optional, not reportable :()). + zb_uint8_t voltage; + // Units of 0.5%. 0x00 (0%) - 0xc8 (100%) (optional, reportable). + zb_uint8_t percentage; +} prst_batt_attrs_t; + +// Soil moisture cluster. +typedef struct { + // 0-100, units of 0.01? + zb_uint16_t percentage; +} prst_soil_moisture_attrs_t; + +// Illuminance cluster - section 4.2.2.2. +typedef struct { + // 10000 * log_10(lux) + 1. + zb_uint16_t log_lux; +} prst_illuminancce_attrs_t; + +struct zb_device_ctx { + zb_zcl_basic_attrs_ext_t basic_attr; + zb_zcl_identify_attrs_t identify_attr; + zb_zcl_temp_measurement_attrs_t temp_measure_attrs; + prst_rel_humidity_attrs_t rel_humidity_attrs; + prst_batt_attrs_t batt_attrs; + prst_soil_moisture_attrs_t soil_moisture_attrs; + prst_illuminancce_attrs_t illuminance_attrs; +}; + +void prst_zb_attrs_init(struct zb_device_ctx *dev_ctx); + +zb_zcl_status_t prst_zb_set_attr_value(zb_uint16_t cluster_id, zb_uint16_t attr_id, void *data); + +#endif // _PRST_ZB_ATTRS_H_ diff --git a/code/nrf-connect/samples/zigbee/src/prst_zb_endpoint_defs.h b/code/nrf-connect/samples/zigbee/src/prst_zb_endpoint_defs.h new file mode 100644 index 0000000..f66f877 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/src/prst_zb_endpoint_defs.h @@ -0,0 +1,103 @@ +#ifndef _PRST_ZB_H_ +#define _PRST_ZB_H_ + +#include "prst_zb_soil_moisture_defs.h" + +#define PRST_ZIGBEE_ENDPOINT 10 +#define PRST_BASIC_MANUF_NAME "b-parasite" + +#define PRST_ZB_DEVICE_ID 0x0008 +#define PRST_ZB_DEVICE_VERSION 0 +#define PRST_ZB_IN_CLUSTER_NUM 7 +#define PRST_ZB_OUT_CLUSTER_NUM 0 +#define PRST_ZB_CLUSTER_NUM (PRST_ZB_IN_CLUSTER_NUM + PRST_ZB_OUT_CLUSTER_NUM) +#define PRST_ZB_ATTR_REPORTING_COUNT 5 + +#define PRST_ZB_DECLARE_CLUSTER_LIST( \ + cluster_list_name, \ + basic_attr_list, \ + identify_attr_list, \ + temp_measurement_attr_list, \ + rel_humidity_attr_list, \ + batt_att_list, \ + soil_moisture_attr_list, \ + illuminance_attr_list) \ + zb_zcl_cluster_desc_t cluster_list_name[] = \ + { \ + ZB_ZCL_CLUSTER_DESC( \ + ZB_ZCL_CLUSTER_ID_IDENTIFY, \ + ZB_ZCL_ARRAY_SIZE(identify_attr_list, zb_zcl_attr_t), \ + (identify_attr_list), \ + ZB_ZCL_CLUSTER_SERVER_ROLE, \ + ZB_ZCL_MANUF_CODE_INVALID), \ + ZB_ZCL_CLUSTER_DESC( \ + ZB_ZCL_CLUSTER_ID_BASIC, \ + ZB_ZCL_ARRAY_SIZE(basic_attr_list, zb_zcl_attr_t), \ + (basic_attr_list), \ + ZB_ZCL_CLUSTER_SERVER_ROLE, \ + ZB_ZCL_MANUF_CODE_INVALID), \ + ZB_ZCL_CLUSTER_DESC( \ + ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT, \ + ZB_ZCL_ARRAY_SIZE(temp_measurement_attr_list, zb_zcl_attr_t), \ + (temp_measurement_attr_list), \ + ZB_ZCL_CLUSTER_SERVER_ROLE, \ + ZB_ZCL_MANUF_CODE_INVALID), \ + ZB_ZCL_CLUSTER_DESC( \ + ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT, \ + ZB_ZCL_ARRAY_SIZE(rel_humidity_attr_list, zb_zcl_attr_t), \ + (rel_humi_attr_list), \ + ZB_ZCL_CLUSTER_SERVER_ROLE, \ + ZB_ZCL_MANUF_CODE_INVALID), \ + ZB_ZCL_CLUSTER_DESC( \ + PRST_ZB_ZCL_ATTR_SOIL_MOISTURE_CLUSTER_ID, \ + ZB_ZCL_ARRAY_SIZE(soil_moisture_attr_list, zb_zcl_attr_t), \ + (soil_moisture_attr_list), \ + ZB_ZCL_CLUSTER_SERVER_ROLE, \ + ZB_ZCL_MANUF_CODE_INVALID), \ + ZB_ZCL_CLUSTER_DESC( \ + ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT, \ + ZB_ZCL_ARRAY_SIZE(illuminance_attr_list, zb_zcl_attr_t), \ + (illuminance_attr_list), \ + ZB_ZCL_CLUSTER_SERVER_ROLE, \ + ZB_ZCL_MANUF_CODE_INVALID), \ + ZB_ZCL_CLUSTER_DESC( \ + ZB_ZCL_CLUSTER_ID_POWER_CONFIG, \ + ZB_ZCL_ARRAY_SIZE(batt_attr_list, zb_zcl_attr_t), \ + (batt_attr_list), \ + ZB_ZCL_CLUSTER_SERVER_ROLE, \ + ZB_ZCL_MANUF_CODE_INVALID)} + +#define PRST_ZB_DECLARE_SIMPLE_DESC(ep_name, ep_id, in_clust_num, out_clust_num) \ + ZB_DECLARE_SIMPLE_DESC(in_clust_num, out_clust_num); \ + ZB_AF_SIMPLE_DESC_TYPE(in_clust_num, out_clust_num) \ + simple_desc_##ep_name = \ + { \ + ep_id, \ + ZB_AF_HA_PROFILE_ID, \ + PRST_ZB_DEVICE_ID, \ + PRST_ZB_DEVICE_VERSION, \ + 0, \ + in_clust_num, \ + out_clust_num, \ + { \ + ZB_ZCL_CLUSTER_ID_BASIC, \ + ZB_ZCL_CLUSTER_ID_IDENTIFY, \ + ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT, \ + ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT, \ + PRST_ZB_ZCL_ATTR_SOIL_MOISTURE_CLUSTER_ID, \ + ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT, \ + ZB_ZCL_CLUSTER_ID_POWER_CONFIG, \ + }} + +#define PRST_ZB_DECLARE_ENDPOINT(ep_name, ep_id, cluster_list) \ + ZBOSS_DEVICE_DECLARE_REPORTING_CTX(reporting_ctx_##ep_name, PRST_ZB_ATTR_REPORTING_COUNT); \ + PRST_ZB_DECLARE_SIMPLE_DESC(ep_name, ep_id, \ + PRST_ZB_IN_CLUSTER_NUM, PRST_ZB_OUT_CLUSTER_NUM); \ + ZB_AF_DECLARE_ENDPOINT_DESC(ep_name, ep_id, ZB_AF_HA_PROFILE_ID, \ + /*reserved_length=*/0, /*reserved_ptr=*/NULL, \ + ZB_ZCL_ARRAY_SIZE(cluster_list, zb_zcl_cluster_desc_t), cluster_list, \ + (zb_af_simple_desc_1_1_t *)&simple_desc_##ep_name, \ + PRST_ZB_ATTR_REPORTING_COUNT, reporting_ctx_##ep_name, \ + /*lev_ctrl_count=*/0, /*lev_ctrl_ctx=*/NULL) + +#endif // _PRST_ZB_H_ diff --git a/code/nrf-connect/samples/zigbee/src/prst_zb_soil_moisture_defs.c b/code/nrf-connect/samples/zigbee/src/prst_zb_soil_moisture_defs.c new file mode 100644 index 0000000..922a093 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/src/prst_zb_soil_moisture_defs.c @@ -0,0 +1,15 @@ +#include "prst_zb_soil_moisture_defs.h" + +#include + +void prst_zcl_soil_moisture_init_server(void) { + zb_zcl_add_cluster_handlers(PRST_ZB_ZCL_ATTR_SOIL_MOISTURE_CLUSTER_ID, + ZB_ZCL_CLUSTER_SERVER_ROLE, + /*cluster_check_value=*/NULL, + /*cluster_write_attr_hook=*/NULL, + /*cluster_handler=*/NULL); +} + +void prst_zcl_soil_moisture_init_client(void) { + // Nothing. +} diff --git a/code/nrf-connect/samples/zigbee/src/prst_zb_soil_moisture_defs.h b/code/nrf-connect/samples/zigbee/src/prst_zb_soil_moisture_defs.h new file mode 100644 index 0000000..8c01ae2 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/src/prst_zb_soil_moisture_defs.h @@ -0,0 +1,40 @@ +#ifndef _PRST_ZB_SOIL_MOISTURE_DEFS_ +#define _PRST_ZB_SOIL_MOISTURE_DEFS_ + +#include +#include + +// Most defines in this file are updated from the ZB_ZCL_DECLARE_TEMP_MEASUREMENT_ATTRIB_LIST, +// adapting attributes and IDs to match the mSoilMoisture cluster spec. +// Values from https://github.com/Koenkk/zigbee-herdsman/blob/master/src/zcl/definition/cluster.ts#L2570 +// (msSoilMoisture). +// Cluster attributes definitions in https://www.st.com/resource/en/user_manual/um2977-stm32wb-series-zigbee-cluster-library-api-stmicroelectronics.pdf. + +#define PRST_ZB_ZCL_ATTR_SOIL_MOISTURE_CLUSTER_ID 1032 + +// Soil moisture value represented as an uint16. +#define PRST_ZB_ZCL_ATTR_SOIL_MOISTURE_VALUE_ID 0x00 + +// Required callbacks. ZBOSS will call these. +void prst_zcl_soil_moisture_init_server(void); +void prst_zcl_soil_moisture_init_client(void); + +#define PRST_ZB_ZCL_ATTR_SOIL_MOISTURE_CLUSTER_ID_SERVER_ROLE_INIT prst_zcl_soil_moisture_init_server +#define PRST_ZB_ZCL_ATTR_SOIL_MOISTURE_CLUSTER_ID_CLIENT_ROLE_INIT prst_zcl_soil_moisture_init_client + +#define PRST_ZB_ZCL_SOIL_MOISTURE_CLUSTER_REVISION_DEFAULT ((zb_uint16_t)0x42) + +#define ZB_SET_ATTR_DESCR_WITH_PRST_ZB_ZCL_ATTR_SOIL_MOISTURE_VALUE_ID(data_ptr) \ + { \ + PRST_ZB_ZCL_ATTR_SOIL_MOISTURE_VALUE_ID, \ + ZB_ZCL_ATTR_TYPE_U16, \ + ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_REPORTING, \ + (void*)data_ptr \ + } + +#define PRST_ZB_ZCL_DECLARE_SOIL_MOISTURE_ATTRIB_LIST(attr_list, value) \ + ZB_ZCL_START_DECLARE_ATTRIB_LIST_CLUSTER_REVISION(attr_list, PRST_ZB_ZCL_SOIL_MOISTURE) \ + ZB_ZCL_SET_ATTR_DESC(PRST_ZB_ZCL_ATTR_SOIL_MOISTURE_VALUE_ID, (value)) \ + ZB_ZCL_FINISH_DECLARE_ATTRIB_LIST + +#endif // _PRST_ZB_SOIL_MOISTURE_DEFS_ \ No newline at end of file diff --git a/code/nrf-connect/samples/zigbee/zigbee.code-workspace b/code/nrf-connect/samples/zigbee/zigbee.code-workspace new file mode 100644 index 0000000..44c0e47 --- /dev/null +++ b/code/nrf-connect/samples/zigbee/zigbee.code-workspace @@ -0,0 +1,16 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "../../prstlib" + } + ], + "settings": { + "C_Cpp.autoAddFileAssociations": false, + "nrf-connect.applications": [ + "${workspaceFolder}" + ] + } +} \ No newline at end of file