Compare commits

...
Sign in to create a new pull request.

69 commits

Author SHA1 Message Date
rbaron
5214f904f1
Merge pull request #214 from ryanrolds/rolds/msec_sleep
Adjusting sleep durations to be milliseconds. Needed for FCC 15b testing.
2024-09-06 09:08:33 +02:00
Ryan R. Olds
20693143ae Fixing build failure 2024-09-05 13:27:16 -07:00
Ryan R. Olds
2e5f89b228 Adjusting code 2024-09-03 16:25:14 -07:00
Ryan R. Olds
2dc7144de1 Switching sleep to to msec. Will aid FCC 15b testing 2024-09-03 16:23:28 -07:00
rbaron
5f671b0170
Merge pull request #207 from seime/debug_log_photo
Log photo adc value, not soil adc
2024-06-04 19:02:55 +02:00
Arne Seime
21dd14d7f3 Remove obsolete logging of soil voltage (always 0) 2024-06-04 16:55:30 +02:00
rbaron
f8c28cebd6
Merge pull request #208 from mrred2k/main
Add link to new case from Printables to README.md
2024-06-04 09:19:08 +02:00
rbaron
8ef2b5b748
Update README.md
- Parasite -> b-parasite
- Link directly to English website instead of german
2024-06-04 09:08:42 +02:00
mrred2k
4e0f3911bd Added a new case 2024-06-03 21:30:17 +02:00
Arne Seime
20d98d02a0 Log photo adc value, not soil adc 2024-06-03 10:21:51 +02:00
rbaron
ccdbaaf142
Merge pull request #194 from mrred2k/main
Include Printables link to 3D printable holder in README.md
2024-04-30 08:02:30 +02:00
mrred2k
57cf5aa0e5 Fixed Typos, deleted model 2024-04-29 21:18:27 +02:00
rbaron
e7e4b77eef
Merge pull request #188 from jrhbcn/main
Report battery percentage in BLE BTHOME_V2 protocol
2024-04-20 13:49:11 +02:00
jrhbcn
dc02480da9
Update encoding.c to fix formatting errors 2024-04-20 13:41:03 +02:00
jrhbcn
4796bfab5f
Update encoding.c
Changes in BTHOME_V2 protocol:
- Reduce resolution of humidity and moisture to 1 byte
- Add battery percentage
2024-04-20 13:10:36 +02:00
rbaron
e62521d2ae
Merge pull request #177 from ScottG489/patch-1
Small typo fix in BLE sample README
2024-03-07 19:15:37 +01:00
rbaron
16c4d23c47
Merge pull request #182 from oleo65/change-d1-component-part-no
Change d1 component part no
2024-03-04 19:07:17 +01:00
Ole Odendahl
15920b1ae4
Reflected changes to D1 in kicad files. 2024-02-25 16:03:26 +01:00
Ole Odendahl
d26eab5825
Updated BOM to changed inventory of JLCPCB.
Changed D1 to equivalent component.
2024-02-25 15:57:46 +01:00
rbaron
c7aaf5b388
Merge pull request #180 from jhbruhn/patch-1 2024-02-14 23:05:36 +01:00
Jan-Henrik Bruhn
905978e8c9
Only read positive voltages in adc.c
The ADC sometimes responds with negative voltages read on the ADC input due to noise. This can lead to very high illuminance readings at night (4095 << 4 to be exact due to two's complement :) ).
This commit caps ADC readings at 0.
2024-02-14 11:59:14 +01:00
Scott Giminiani
4c0fe9b4c6
Small typo fix in BLE sample README 2024-01-07 09:20:30 -08:00
rbaron
dd373ea235
Merge pull request #176 from victorhooi/patch-1
Fix broken link for ALS-PT19 datasheet
2024-01-01 22:53:51 +01:00
Victor Hooi
25a5941777
Fix broken link for ALS-PT19 datasheet 2024-01-01 23:02:42 +11:00
rbaron
37ea3a1062
Merge pull request #165 from rbaron/nrf2.5
Update to nRF Connect SDK 2.5
2023-11-08 08:33:01 +01:00
rbaron
d3ddd089bd
Merge pull request #166 from rbaron/nrf52833-flashfix
Fix nRF52833 flash partitions
2023-11-08 08:32:27 +01:00
rbaron
28c2809b04 Fix nRF52833 flash partitions
The nRF52833 has only 512 kB of flash. The .dts file was incorrect, as a
product of having it forked from the nRF52840 .dts files, which has 1 MB
of flash.

The new, correct partitions are closely related to the
[nrf52833dk](ec52722861/boards/arm/nrf52833dk_nrf52833/nrf52833dk_nrf52833.dts (L215)).
2023-11-07 21:32:09 +01:00
rbaron
ad3cd4194f Remove PM configs from soil_read_loop 2023-11-07 21:18:20 +01:00
rbaron
171a1eb4a0 Uses newwer FIXED_PARTITION_ID for fs_mount_t
... This is what new samples use, but there should be no functional
difference.
2023-11-07 20:57:54 +01:00
rbaron
5199ceaa50 Update to nRF Connect SDK 2.5 2023-11-07 18:31:32 +01:00
rbaron
7de5115705
Merge pull request #163 from rbaron/nrf-2.4.2
Bump nrf-connect sdk version to 2.4
2023-10-21 17:34:02 +02:00
rbaron
d5ed701c45 Simplify actions -- no artifacts 2023-10-21 17:26:55 +02:00
rbaron
141071a414 Refactor github actions
Reason: https://github.com/NordicPlayground/nrf-docker/pull/75
2023-10-21 17:21:39 +02:00
rbaron
35618227d5 Also bump in devcontainer.json 2023-10-21 10:30:36 +02:00
rbaron
77c204397e Bump nrf-connect sdk version to 2.4.2
Manually tested with both ble and zigbee samples.
2023-10-21 10:28:18 +02:00
mrred2k
5d2b648b22 Added the link to the holder
Added the 3d Printed holder from printables. Also available in "case"
2023-08-29 11:30:49 +02:00
mrred2k
da1642514a Created a holder for 10 b-parasides
Created a simple 3d printable holder for dealing with a bigger number of b-parasides.
2023-08-29 11:22:59 +02:00
rbaron
85ca1a67bc
Merge pull request #153 from rbaron/ble-bthomev2-def
[ble sample] Sets BTHome V2 as the default encoding
2023-07-23 18:45:15 +02:00
rbaron
e68c113513 [ble sample] Sets BTHome V2 as the default encoding
This change has been planned for a while, and BTHome v2 + Home Assistant
is currently the most convenient, hassle-free and maintained way to
interface with the BLE sample currently.
2023-07-23 18:39:01 +02:00
rbaron
e0481994b2
Merge pull request #152 from rbaron/ble-assert
[ble] __ASSERT initialization & sensor reading
2023-07-23 18:35:15 +02:00
rbaron
9ea5c7b4d0 Make init & loop fns static 2023-07-23 18:30:23 +02:00
rbaron
1a5acd02a5 [ble] __ASSERT initialization & sensor reading
After we tracked down a rare, annoying bug for the zigbee sample in #150,
this PR does something similar for the ble sample. If there's an error
either initializing the board of reading the sensors, __ASSERT will
reboot the system, instead of letting it hang forever.
2023-07-23 18:10:28 +02:00
rbaron
8fb45f7165
Merge pull request #150 from rbaron/zb-wdt
[zigbee sample] Fix rare infinite loop bug & introduce watchdog
2023-07-23 17:51:21 +02:00
rbaron
9fdd15626a Better error handling for SHTC3 reading 2023-07-15 11:07:35 +02:00
rbaron
c70eb600a3 [zigbee sample] Introduce watchdog for sensor task 2023-07-15 09:45:34 +02:00
rbaron
95ec660cb6
Merge pull request #146 from rbaron/zb-debug++
Fix (minor?) issues with the ZigBee sample
2023-07-15 09:25:18 +02:00
rbaron
d84a6422fe
Merge pull request #148 from Judman/main
Slightly Modified High Airflow Case
2023-07-03 08:05:06 +02:00
Judman
8c21fe73d5
Replace Current High Airflow Cases 2023-07-02 11:33:26 -05:00
Judman
0a73159ce9
Delete b_parasite_case_high_airflow_v2.stl 2023-07-02 11:29:08 -05:00
Judman
105016e24c
Slightly Modified High Airflow Case
Removed the circular supports the extended all the way to the build plate and left small bumps at the top of the case. Replaced with slightly wider and flatter supports that fit within the case constraints. Also slightly enlarged the PCB groove. I found it way too tight and ended up ripping off a batter holder and pads during removal.
2023-07-01 22:41:25 -05:00
rbaron
bc3d834b20 Make it mandatory to select a factory reset method
... in order to avoid unhappy surprises
2023-07-01 08:26:21 +02:00
rbaron
37aff683ad LOG_INF -> LOG_DBG 2023-07-01 08:04:17 +02:00
rbaron
c86f04e406 Nuclear reset option if recurring sensor task cannot be rescheduled
Famous last words: "this should never happen"
2023-07-01 08:04:17 +02:00
rbaron
7f42dd0c00 Add missing prst_led_off after stopping LED flashing timer 2023-07-01 08:04:17 +02:00
rbaron
46feb542cc More fixes to the ZigBee sample
- 🚨 Missing `break` statements in `zboss_signal_handler`
- Makes `zboss_signal_handler` return fast. According to the
  [docs](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.thread_zigbee.v2.0.0%2Fgroup__zb__comm__signals.html):
  "Signal processing should not do long operations synchronously". I
  removed calls to `prst_flash_led` (essentially a huge sync delay) and `debug_counters_increment`,
  which were blocking. I'll try migrating the debug_counters to
  non-blocking separately
- Main sensor reading task is started for the first time upon
  the `ZB_ZDO_SIGNAL_SKIP_STARTUP` signal. This is what the
  [weather_station sample](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/applications/zigbee_weather_station/README.html) does. It should have no functional difference, but there's [some special handling for alarms started from the ZigBee task](0469247dc4/subsys/zigbee/osif/zb_nrf_platform.c (L426))
2023-07-01 08:04:17 +02:00
rbaron
62a99cc716 Reintroduced accidentally deleted comment 2023-07-01 08:04:17 +02:00
rbaron
90f99bd5b0 Fixes (minor?) issues with the ZigBee sample
1. zigbee_configure_sleepy_behavior must be called before zigbee_enable - [source
docs](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/libraries/zigbee/zigbee_app_utils.html#c.zigbee_configure_sleepy_behavior)

2. zb_zdo_pim_set_long_poll_interval must be called after the network is joined - [source docs](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/zboss/3.5.2.0/group__zdo__poll__control.html#ga3aae8929b30e71c872f937771b89c768).
On top of that, the argument for the call was incorrect - it expects ms directly.

3. Removed the unecessary CONFIG_PM, seems like there's no impact in power consumption. Could it cause some weirdness?

4. Implemented CONFIG_PRST_ZB_FACTORY_RESET_DISABLED=y to fully disable factory resetting. May aid debugging.

5. Implemented multi/single channel support. Helps with battery life when scanning networks
2023-07-01 08:04:15 +02:00
rbaron
5882312a77 [zb] Debugging changes:
- Disables double reset
- Fully reset with zb_reset() on reset watchdog instead of
  user_input_indicate()
- Debug counters before/after sensors read
2023-07-01 08:03:42 +02:00
rbaron
a4a02257f9
Merge pull request #147 from rbaron/zb-sw1-factory-reset
[ZigBee sample] Implements factory reset via SW1 button (hardware v2.0.0+)
2023-07-01 07:55:45 +02:00
rbaron
f9f122b818 Formatting 2023-06-27 21:34:49 +02:00
rbaron
d5891f4a6f [ZigBee sample] Implements factory reset via SW1 button (hardware v2.0.0+) 2023-06-27 21:28:56 +02:00
rbaron
56c5fa7ba0
Merge pull request #133 from rbaron/zb_debug_counters
[ZigBee Sample] Introduce debug counters
2023-06-27 19:08:53 +02:00
rbaron
f6b4600044
Merge pull request #140 from rbaron/config_board_revision_code
Introduce CONFIG_BOARD_REVISION_CODE
2023-06-15 18:36:22 +02:00
rbaron
afa53f0e94 Introduce CONFIG_BOARD_REVISION_CODE
Beside having a `CONFIG_BOARD_REVISION` Kconfig string that represents
our semantic versioning for board revisions, the ZigBee sample requires
an integer representation of it. Instead of conditioning the integer
version on the string version only for that sample and having to touch
the ZigBee sample whenever we add a revision, I decided to have a
board-wide config, so that both string and int versions live close to
each other.

Fixes #138.
2023-06-15 07:45:55 +02:00
rbaron
426195184d
Merge pull request #136 from rbaron/code-of-conduct
Create CODE_OF_CONDUCT.md
2023-06-09 18:58:59 +02:00
rbaron
930239e385 Cleanup + double_reset counter 2023-06-07 08:26:05 +02:00
rbaron
015ce94bbd Cleanup 2023-06-07 08:10:23 +02:00
rbaron
987c4f52c0 Removes async debug counters API - not needed 2023-06-07 08:08:34 +02:00
rbaron
509350457f [ZigBee sample] Introduce debug counters
In the effort to increse reliability of the Zigbee sample, #126 and #130
introduced handling for possibly rare, hard-to-debug events that may be
occurring. While these changes seem to help in practive, we don't know
exactly how often, if ever, they are triggered.

This PR introduces debug_counters in hope to add some visilibty in these
hard-to-debug, rare conditions.

Calling `prst_debug_counters_increment("some_counter")` will increment a
value stored in flash. These values are then dumped via logging when the
device boots. Moving forward we can also consider exposing these values
via a ZigBee cluster.
2023-06-07 08:08:34 +02:00
47 changed files with 578 additions and 285 deletions

View file

@ -1,6 +1,6 @@
{
"name": "nrf-connect:v2.2",
"image": "nordicplayground/nrfconnect-sdk:v2.2-branch",
"name": "nrf-connect:v2.4",
"image": "nordicplayground/nrfconnect-sdk:v2.5-branch",
"features": {
},
"customizations": {

View file

@ -1,5 +0,0 @@
FROM nordicplayground/nrfconnect-sdk:v2.2-branch
COPY build.sh /build.sh
ENTRYPOINT ["/build.sh"]

View file

@ -1,27 +1,27 @@
name: 'Build'
description: 'Builds a nrf-connect sample for b-parasite'
name: "Build"
description: "Builds a nrf-connect sample for b-parasite"
inputs:
sample-dir:
description: 'Sample directory to build'
description: "Sample directory to build"
required: true
board:
description: 'Board definition to use'
description: "Board definition to use"
default: bparasite_nrf52840
revision:
description: 'Board revision use'
default: '1.2.0'
description: "Board revision use"
default: "2.0.0"
cmake-extra:
description: 'Extra CMake arguments'
default: ''
output-bin:
description: 'Name of the .hex output'
required: true
description: "Extra CMake arguments"
default: ""
runs:
using: 'docker'
image: 'Dockerfile'
args:
- ${{ inputs.sample-dir }}
- ${{ inputs.board }}
- ${{ inputs.revision }}
- ${{ inputs.cmake-extra }}
- ${{ inputs.output-bin }}
using: "composite"
steps:
- run: |
docker run --rm -v ${GITHUB_WORKSPACE}:/repo \
nordicplayground/nrfconnect-sdk:v2.5-branch \
west build \
--build-dir /repo/${{ inputs.sample-dir }}/build \
--pristine \
--board ${{ inputs.board }}@${{ inputs.revision }} \
/repo/${{ inputs.sample-dir }} -- ${{ inputs.cmake-extra }}
shell: bash

View file

@ -1,21 +0,0 @@
#!/bin/bash
set -eux -o pipefail
SAMPLE_DIR=$1
BOARD=$2
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}"

View file

@ -1,33 +0,0 @@
name: 'Build and upload artifact'
description: 'Builds a nrf-connect sample for b-parasite'
inputs:
sample-dir:
description: 'Sample directory to build'
required: true
board:
description: 'Board definition to use'
required: true
revision:
description: 'Board revision use'
default: '1.2.0'
cmake-extra:
description: 'Extra CMake arguments'
default: ''
output-bin:
description: 'Name of the .hex output'
required: true
runs:
using: "composite"
steps:
- uses: ./.github/actions/build
with:
sample-dir: ${{ inputs.sample-dir }}
board: ${{ inputs.board }}
revision: ${{ inputs.revision }}
cmake-extra: ${{ inputs.cmake-extra }}
output-bin: ${{ inputs.output-bin }}
- uses: actions/upload-artifact@v3
with:
name: sample-binaries
path: ${{ inputs.sample-dir }}/build/zephyr/${{ inputs.output-bin }}

View file

@ -17,7 +17,7 @@ jobs:
- name: Check clang-format
uses: jidicula/clang-format-action@v4.9.0
with:
check-path: 'code/nrf-connect'
check-path: "code/nrf-connect"
exclude-regex: '\/build\/'
build-blinky:
@ -27,7 +27,7 @@ jobs:
- lint
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Build blinky
uses: ./.github/actions/build
with:
@ -43,7 +43,7 @@ jobs:
- lint
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Build input
uses: ./.github/actions/build
with:
@ -59,7 +59,7 @@ jobs:
- lint
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Build soil_read_loop
uses: ./.github/actions/build
with:
@ -79,14 +79,14 @@ jobs:
- lint
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Build
uses: ./.github/actions/build_and_upload
uses: ./.github/actions/build
with:
sample-dir: code/nrf-connect/samples/ble
board: bparasite_${{ matrix.soc }}
revision: ${{ matrix.revision }}
cmake-extra: -DCONFIG_PRST_BLE_ENCODING_BTHOME_V2=y -DCONFIG_PRST_SLEEP_DURATION_SEC=1 -DCONFIG_PRSTLIB_LOG_LEVEL_DBG=y
cmake-extra: -DCONFIG_PRST_BLE_ENCODING_BTHOME_V2=y -DCONFIG_PRST_SLEEP_DURATION_MSEC=1000 -DCONFIG_PRSTLIB_LOG_LEVEL_DBG=y
output-bin: ble_${{ matrix.soc }}_${{ matrix.revision }}_debug.hex
build-zigbee:
@ -100,9 +100,9 @@ jobs:
- lint
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Build
uses: ./.github/actions/build_and_upload
uses: ./.github/actions/build
with:
sample-dir: code/nrf-connect/samples/zigbee
board: bparasite_${{ matrix.soc }}

View file

@ -10,7 +10,7 @@ b-parasite is an open source soil moisture and ambient temperature/humidity/ligh
# Features
* Capacitive Soil moisture sensor - see [this blog post](https://rbaron.net/blog/2021/04/05/How-capacitive-soil-moisture-sensors-work.html), [this Twitter thread](https://twitter.com/rbaron_/status/1367182806368071685), and [this post](https://wemakethings.net/2012/09/26/capacitance_measurement/) for nice resources on how they work
* Air temperature and humidity sensor using a [Sensirion's SHTC3](https://www.sensirion.com/en/environmental-sensors/humidity-sensors/digital-humidity-sensor-shtc3-our-new-standard-for-consumer-electronics/)
* Light sensor using an [ALS-PT19](https://everlighteurope.com/ambient-light-sensors/7/ALSPT19315CL177TR8.html) phototransistor
* Light sensor using an [ALS-PT19](https://en.everlight.com/wp-content/plugins/ItemRelationship/product_files/pdf/ALS-PT19-315C-L177-TR8_V8.pdf) phototransistor
* Powered by a common CR2032 coin cell, potentially for over two years
* Support for [nRF52840](https://www.nordicsemi.com/products/nrf52840) and [nRF52833](https://www.nordicsemi.com/products/nrf52833) modules
* Open hardware and open source design
@ -20,7 +20,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/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/) + [BTHome](https://bthome.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)| Reads the soil moisture sensor on a loop. Useful for experimenting and calibrating the sensor. |-|
@ -49,6 +49,12 @@ We have three different 3D-printable cases:
1. Original snap-on case - [case/Top.stl](./case/Top.stl), [case/Bottom.stl](./case/Bottom.stl)
2. High airflow - [case/b_parasite_case_high_airflow.stl](./case/b_parasite_case_high_airflow.stl)
3. Mushroom-style - available on [Printables](https://www.printables.com/model/456571-mushroomcap-for-b-parasite-soil-moisture-sensor)
4. b-parasite Hat - available on [Printables](https://www.printables.com/model/901220-waterproof-case-for-b-parasite-soil-moisture-air-s)
# Accessories
Designs and hardware to help you, when building your own:
1. Desk holder for b-parasites [Printables](https://www.printables.com/de/model/566974-b-parasite-holder)
# License
The hardware and associated design files are released under the [Creative Commons CC BY-SA 4.0 license](https://creativecommons.org/licenses/by-sa/4.0/).

View file

@ -8,6 +8,10 @@ config BOARD_REVISION
string "Board revision."
default "1.0.0"
config BOARD_REVISION_CODE
int "Board revision code. An integer representation of the board revision."
default 1
config BT_CTLR
default BT

View file

@ -56,6 +56,10 @@
};
};
&uicr {
gpio-as-nreset;
};
&gpiote {
status = "okay";
};
@ -135,7 +139,6 @@
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
@ -143,33 +146,23 @@
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x00000000 0x0000C000>;
reg = <0x000000000 0xC000>;
};
slot0_partition: partition@c000 {
label = "image-0";
reg = <0x0000C000 0x00067000>;
reg = <0x0000C000 0x32000>;
};
slot1_partition: partition@73000 {
slot1_partition: partition@3e000 {
label = "image-1";
reg = <0x00073000 0x00067000>;
reg = <0x0003E000 0x32000>;
};
scratch_partition: partition@da000 {
scratch_partition: partition@70000 {
label = "image-scratch";
reg = <0x000da000 0x0001e000>;
reg = <0x00070000 0xA000>;
};
/*
* The flash starting at 0x000f8000 and ending at
* 0x000fffff is reserved for use by the application.
*/
/*
* Storage partition will be used by FCB/LittleFS/NVS
* if enabled.
*/
storage_partition: partition@f8000 {
storage_partition: partition@7a000 {
label = "storage";
reg = <0x000f8000 0x00008000>;
reg = <0x0007A000 0x00006000>;
};
};
};
};

View file

@ -1 +1,2 @@
CONFIG_BOARD_REVISION="1.1.0"
CONFIG_BOARD_REVISION_CODE=2

View file

@ -1 +1,2 @@
CONFIG_BOARD_REVISION="1.2.0"
CONFIG_BOARD_REVISION_CODE=3

View file

@ -1 +1,2 @@
CONFIG_BOARD_REVISION="2.0.0"
CONFIG_BOARD_REVISION_CODE=4

View file

@ -16,6 +16,4 @@ CONFIG_GPIO=y
CONFIG_CONSOLE=y
CONFIG_RTT_CONSOLE=y
CONFIG_GPIO_AS_PINRESET=y
CONFIG_PINCTRL=y

View file

@ -8,6 +8,10 @@ config BOARD_REVISION
string "Board revision."
default "1.0.0"
config BOARD_REVISION_CODE
int "Board revision code. An integer representation of the board revision."
default 1
config BT_CTLR
default BT

View file

@ -56,6 +56,10 @@
};
};
&uicr {
gpio-as-nreset;
};
&gpiote {
status = "okay";
};

View file

@ -1 +1,2 @@
CONFIG_BOARD_REVISION="1.1.0"
CONFIG_BOARD_REVISION_CODE=2

View file

@ -1 +1,2 @@
CONFIG_BOARD_REVISION="1.2.0"
CONFIG_BOARD_REVISION_CODE=3

View file

@ -1 +1,2 @@
CONFIG_BOARD_REVISION="2.0.0"
CONFIG_BOARD_REVISION_CODE=4

View file

@ -16,6 +16,4 @@ CONFIG_GPIO=y
CONFIG_CONSOLE=y
CONFIG_RTT_CONSOLE=y
CONFIG_GPIO_AS_PINRESET=y
CONFIG_PINCTRL=y

View file

@ -108,6 +108,7 @@ static int read_adc_spec(const struct adc_dt_spec* spec, prst_adc_read_t* out) {
int32_t val_mv = buf;
RET_IF_ERR(adc_raw_to_millivolts_dt(spec, &val_mv));
val_mv = MAX(0, val_mv);
out->raw = buf;
out->millivolts = val_mv;

View file

@ -16,10 +16,9 @@ int prst_sensors_read_all(prst_sensors_t *sensors) {
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("Soil: %.0f %%", 100 * sensors->soil.percentage);
LOG_DBG("Photo: %u lx (%d mV)", sensors->photo.brightness,
sensors->soil.adc_read.millivolts);
sensors->photo.adc_read.millivolts);
LOG_DBG("Temp: %f oC", sensors->shtc3.temp_c);
LOG_DBG("Humi: %.0f %%", 100 * sensors->shtc3.rel_humi);
LOG_DBG("--------------------------------------------------");

View file

@ -32,11 +32,10 @@ int prst_shtc3_read(prst_shtc3_read_t *out) {
// Reading in normal (not low power) mode can take up to 12.1 ms, according to
// the datasheet.
k_msleep(15);
k_msleep(20);
while (i2c_read_dt(&shtc3, buff, 6) != 0) {
k_msleep(10);
}
// Read response.
RET_IF_ERR(i2c_read_dt(&shtc3, buff, 6));
// Put the sensor in sleep mode.
RET_IF_ERR(write_cmd(PRST_SHTC3_CMD_SLEEP));

View file

@ -2,13 +2,13 @@
source "Kconfig.zephyr"
rsource "../../prstlib/Kconfig"
config PRST_SLEEP_DURATION_SEC
int "Sleep duration in seconds"
default 600
config PRST_SLEEP_DURATION_MSEC
int "Sleep duration in milliseconds"
default 600000
config PRST_BLE_ADV_DURATION_SEC
int "Advertising duration in seconds"
default 1
config PRST_BLE_ADV_DURATION_MSEC
int "Advertising duration in milliseconds"
default 1000
config PRST_BLE_MIN_ADV_INTERVAL
int "Minimum advertising interval in milliseconds"
@ -20,7 +20,7 @@ config PRST_BLE_MAX_ADV_INTERVAL
choice PRST_BLE_ENCODING
prompt "b-parasite BLE encoding"
default PRST_BLE_ENCODING_BPARASITE_V2
default PRST_BLE_ENCODING_BTHOME_V2
config PRST_BLE_ENCODING_BPARASITE_V2
bool "Uses the custom b-parasite protocol v2 for encoding advertising packets"

View file

@ -1,5 +1,5 @@
# Bluetooth Low Energy (BLE)
In this saple, b-parasite sensors are periodically read and broadcast using Bluetooth Low Energy (BLE) dvertising packets.
In this sample, b-parasite sensors are periodically read and broadcast using Bluetooth Low Energy (BLE) dvertising packets.
## Configuration
Available configurations and their default values are in [`Kconfig`](./Kconfig). They are set in [`prj.conf`](./prj.conf). Here are some notable examples.
@ -8,7 +8,7 @@ Available configurations and their default values are in [`Kconfig`](./Kconfig).
To save energy, the board spends most of the time in a "deep sleep" state, in which most peripherals and radio are completely turned off. The period of sleep is controlled by the `PRST_SLEEP_DURATION_SEC` config.
### Advertising Duration
When it wakes up, the sample reads all sensors and keep broadcasting advertising packets for `PRST_BLE_ADV_DURATION_SEC` before going back to sleep.
When it wakes up, the sample reads all sensors and keep broadcasting advertising packets for `PRST_BLE_ADV_DURATION_MSEC` before going back to sleep.
### Advertising Packet Encoding
There are different ways to encode the sensor data in a BLE advertising packet.
@ -22,11 +22,10 @@ This is what a typical deployment with BTHome looks like:
![Topology with BTHome + Home Assistant](./media/drawings/ble-bthome-encoding.png)
There are two versions of BTHome encodings supported by this sample:
* `PRST_BLE_ENCODING_BTHOME_V2=y` (**default**) uses the [BTHome V2](https://bthome.io/format/), supported by Home Assistant since version `2022.12`
* `PRST_BLE_ENCODING_BTHOME_V1=y` uses the [legacy BTHome V1](https://bthome.io/v1/), which was briefly in use
* `PRST_BLE_ENCODING_BTHOME_V2=y` uses the [BTHome V2](https://bthome.io/format/), which is not yet (as of early Dec/2022) in the stable Home Assistant release. This will soon become the default.
#### b-parasite Encoding
`PRST_BLE_ENCODING_BPARASITE_V2=y` selects the "legacy" encoding, used historically inn this project. This is the encoding that the [`b_parasite`](https://esphome.io/components/sensor/b_parasite.html) ESPHome component understands.
`PRST_BLE_ENCODING_BPARASITE_V2=y` selects the legacy encoding, used historically in this project. This is the encoding that the [`b_parasite`](https://esphome.io/components/sensor/b_parasite.html) ESPHome component understands.
With this encoding and a ESPHome + `b_parasite` component, this is an usual deployment topology:
@ -53,4 +52,4 @@ If for example we lower the connection interval to the SDK defaults (`[100, 150]
![Broadcasting with 100ms current consumption](./media/power-profile/broadcasting-100.png)
With a `200 mAh` CR2032 battery, we can use [this spreadsheet](https://docs.google.com/spreadsheets/d/157JQiX20bGkTrlbvWbWRrs_WViL3MgVZffSCWRR7uAI/edit#gid=0) to estimate the battery life to over two years. Note that this is a simplified model and results in practice may vary.
With a `200 mAh` CR2032 battery, we can use [this spreadsheet](https://docs.google.com/spreadsheets/d/157JQiX20bGkTrlbvWbWRrs_WViL3MgVZffSCWRR7uAI/edit#gid=0) to estimate the battery life to over two years. Note that this is a simplified model and results in practice may vary.

View file

@ -11,17 +11,17 @@ CONFIG_BT_DEVICE_NAME="prst"
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
CONFIG_SERIAL=n
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y
CONFIG_ASSERT=y
# Application config - see all options in Kconfig.
# CONFIG_PRST_BLE_ENCODING_BTHOME_V2=y
# CONFIG_PRST_SLEEP_DURATION_SEC=1
# CONFIG_PRST_SLEEP_DURATION_MSEC=1000
# prstlib config - ser all options in prstlib/Kconfig.
# CONFIG_PRSTLIB_LOG_LEVEL_DBG=y

View file

@ -104,33 +104,36 @@ int prst_ble_encode_service_data(const prst_sensors_t* sensors,
out[4] = temp_val & 0xff;
out[5] = temp_val >> 8;
// Humidity.
out[6] = 0x03;
// Value. Factor 0.01, over 100%.
uint16_t humi_val = 10000 * sensors->shtc3.rel_humi;
out[7] = humi_val & 0xff;
out[8] = humi_val >> 8;
out[6] = 0x2E;
// Value. Factor 1 over 100%.
uint8_t humi_val = 100 * sensors->shtc3.rel_humi + 0.5f;
out[7] = humi_val;
// Illuminance.
out[9] = 0x05;
out[8] = 0x05;
// Value. Factor of 0.01.
uint32_t lux_val = sensors->photo.brightness * 100;
out[10] = lux_val & 0xff;
out[11] = (lux_val >> 8) & 0xff;
out[12] = (lux_val >> 16) & 0xff;
out[9] = lux_val & 0xff;
out[10] = (lux_val >> 8) & 0xff;
out[11] = (lux_val >> 16) & 0xff;
// Battery voltage.
out[13] = 0x0c;
out[12] = 0x0c;
// Value. Factor of 0.001.
uint16_t batt_val = sensors->batt.adc_read.millivolts;
out[14] = batt_val & 0xff;
out[15] = batt_val >> 8;
out[13] = batt_val & 0xff;
out[14] = batt_val >> 8;
// Soil moisture.
out[16] = 0x14;
// Factor of 0.01, so we need to multiply our the value in 100% by 1/0.01 = 100.
uint16_t soil_val = 10000 * sensors->soil.percentage;
out[17] = soil_val & 0xff;
out[18] = soil_val >> 8;
out[15] = 0x2F;
// Factor of 1 over 100%
uint8_t soil_val = 100 * sensors->soil.percentage + 0.5f;
out[16] = soil_val;
// Battery percentage.
out[17] = 0x01;
// Value. Factor 1 over 100%
uint8_t batt_percentage_val = 100 * sensors->batt.percentage + 0.5f;
out[18] = batt_percentage_val;
#endif // Encoding protocols
LOG_HEXDUMP_DBG(out, out_len, "Encoded BLE adv: ");
return 0;
}
}

View file

@ -14,25 +14,29 @@
LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
int main(void) {
static int prst_init() {
RET_IF_ERR(prst_adc_init());
RET_IF_ERR(prst_led_init());
RET_IF_ERR(prst_button_init());
RET_IF_ERR(prst_ble_init());
return 0;
}
RET_IF_ERR(prst_led_flash(2));
static int prst_loop(prst_sensors_t *sensors) {
RET_IF_ERR(prst_sensors_read_all(sensors));
RET_IF_ERR(prst_ble_adv_set_data(sensors));
RET_IF_ERR(prst_ble_adv_start());
k_msleep(CONFIG_PRST_BLE_ADV_DURATION_MSEC);
RET_IF_ERR(prst_ble_adv_stop());
return 0;
}
int main(void) {
__ASSERT(!prst_init(), "Error in prst_init()");
prst_led_flash(2);
prst_sensors_t sensors;
while (true) {
RET_IF_ERR(prst_sensors_read_all(&sensors));
RET_IF_ERR(prst_ble_adv_set_data(&sensors));
RET_IF_ERR(prst_ble_adv_start());
k_sleep(K_SECONDS(CONFIG_PRST_BLE_ADV_DURATION_SEC));
RET_IF_ERR(prst_ble_adv_stop());
k_sleep(K_SECONDS(CONFIG_PRST_SLEEP_DURATION_SEC));
__ASSERT(!prst_loop(&sensors), "Error in prst_loop()");
k_msleep(CONFIG_PRST_SLEEP_DURATION_MSEC);
}
}

View file

@ -7,8 +7,6 @@ CONFIG_GPIO=y
CONFIG_CBPRINTF_FP_SUPPORT=y
CONFIG_SERIAL=n
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_USE_SEGGER_RTT=y

View file

@ -5,6 +5,4 @@ CONFIG_PWM_LOG_LEVEL_DBG=y
CONFIG_CBPRINTF_FP_SUPPORT=y
CONFIG_ADC=y
CONFIG_GPIO=y
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_USE_SEGGER_RTT=y

View file

@ -12,11 +12,14 @@ include_directories(src)
target_sources(app PRIVATE
src/main.c
src/debug_counters.c
src/double_reset_detector.c
src/factory_reset.c
src/flash_fs.c
src/prst_zb_attrs.c
src/prst_zb_soil_moisture_defs.c
src/restart_handler.c
src/watchdog.c
)
add_subdirectory(../../prstlib prstlib)

View file

@ -7,7 +7,7 @@ config PRST_ZB_SLEEP_DURATION_SEC
config PRST_ZB_PARENT_POLL_INTERVAL_SEC
int "Interval for when b-parasite polls its parent for data in seconds."
default 60
default 10
config PRST_ZB_BUILD_DATE
string "Zigbee basic cluster build date attribute. Max 16 bytes."
@ -19,11 +19,11 @@ config PRST_ZB_MODEL_ID
config PRST_ZB_HARDWARE_VERSION
int "Zigbee basic cluster hardware version attribute. 1 byte."
default 2
default BOARD_REVISION_CODE
choice PRST_ZB_FACTORY_RESET_METHOD
bool "Factory reset method"
default PRST_ZB_FACTORY_RESET_VIA_DOUBLE_RESET
default PRST_ZB_FACTORY_RESET_VIA_SW1
config PRST_ZB_FACTORY_RESET_VIA_DOUBLE_RESET
bool "Double resetting factory resets the device."
@ -31,6 +31,12 @@ config PRST_ZB_FACTORY_RESET_VIA_DOUBLE_RESET
config PRST_ZB_FACTORY_RESET_VIA_RESET_PIN
bool "Resetting via the reset pin will factory reset the device. Power cycling through battery replacement will not."
config PRST_ZB_FACTORY_RESET_VIA_SW1
bool "Resetting while pressing and holding SW1 for 5 seconds will factory reset the device. Only available on v2.0.0+ hardware revisions."
config PRST_ZB_FACTORY_RESET_DISABLED
bool "No factory reset procedure."
endchoice # PRST_ZB_FACTORY_RESET_METHOD
config PRST_ZB_RESTART_WATCHDOG_TIMEOUT_SEC

View file

@ -5,8 +5,8 @@ CONFIG_I2C=y
CONFIG_ADC=y
CONFIG_GPIO=y
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096
CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y
@ -36,8 +36,17 @@ CONFIG_NET_IPV6_RA_RDNSS=n
CONFIG_NET_IP_ADDR_CHECK=n
CONFIG_NET_UDP=n
##
## ZigBee Channel Selection
##
# Get Zigbee to scan every channel.
CONFIG_ZIGBEE_CHANNEL_SELECTION_MODE_MULTI=y
# By default only scans channel 11 (ZigBee2MQTT default) and 15 (ZHA default).
# Comment to scan all channels - this will make pairing consume more energy.
# CONFIG_ZIGBEE_CHANNEL_MASK=0x8800
# Uncomment to set a specific channel - this will make pairing more energy efficient.
# CONFIG_ZIGBEE_CHANNEL=11
# 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
@ -49,4 +58,15 @@ CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y
CONFIG_FILE_SYSTEM_LITTLEFS=y
CONFIG_WATCHDOG=y
CONFIG_HWINFO=y
CONFIG_ASSERT=y
# Uncomment for debug log level.
# CONFIG_LOG_DEFAULT_LEVEL=4
# Factory reset method selection. Only hardware revision 2.0.0+ has button SW1. Earlier
# revisions must select a different method. See Kconfig for options.
# CONFIG_PRST_ZB_FACTORY_RESET_VIA_SW1=y

View file

@ -0,0 +1,106 @@
#include "debug_counters.h"
#include <prstlib/macros.h>
#include <zephyr/fs/fs.h>
#include <zephyr/fs/littlefs.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(debug_counters, CONFIG_LOG_DEFAULT_LEVEL);
static const char kDebugCountersDir[] = "/lfs/debug_counters";
#define PRST_MAX_COUNTER_NAME_LENGTH 64
#define PRST_MAX_FILE_NAME_LENGTH (sizeof(kDebugCountersDir) + 1 + PRST_MAX_COUNTER_NAME_LENGTH)
static int mk_filename(const char* counter_name, char* buff, size_t buff_size) {
RET_CHECK(strlen(counter_name) <= PRST_MAX_COUNTER_NAME_LENGTH, "Counter name too long");
strcpy(buff, kDebugCountersDir);
strcat(buff, "/");
strcat(buff, counter_name);
return 0;
}
static int read_counter_file(struct fs_file_t* file, prst_debug_counter_t* value) {
int n_read = fs_read(file, value, sizeof(prst_debug_counter_t));
if (n_read != sizeof(prst_debug_counter_t)) {
LOG_WRN("fs_read returned %d, expected %d, assuming first access", n_read, sizeof(prst_debug_counter_t));
*value = 0;
}
return 0;
}
int prst_debug_counters_init() {
struct fs_dirent entry;
int err = fs_stat(kDebugCountersDir, &entry);
if (err == 0) {
LOG_DBG("Directory %s already exists", kDebugCountersDir);
return 0;
} else if (err == -ENOENT) {
LOG_DBG("Creating directory %s", kDebugCountersDir);
return fs_mkdir(kDebugCountersDir);
}
LOG_ERR("Unexpected error in fs_stat for %s: %d", kDebugCountersDir, err);
return err;
}
int prst_debug_counters_increment(const char* counter_name) {
char filename[PRST_MAX_FILE_NAME_LENGTH];
RET_IF_ERR(mk_filename(counter_name, filename, sizeof(filename)));
LOG_DBG("Incrementing counter %s", filename);
// Open the file.
struct fs_file_t file;
fs_file_t_init(&file);
RET_IF_ERR(fs_open(&file, filename, FS_O_CREATE | FS_O_RDWR));
// Read the current value.
prst_debug_counter_t value;
RET_IF_ERR(read_counter_file(&file, &value));
// Increment the value.
value++;
// Write back to file.
RET_CHECK(fs_seek(&file, 0, SEEK_SET) == 0, "Unable to seek");
ssize_t written = fs_write(&file, &value, sizeof(value));
if (written != sizeof(value)) {
LOG_ERR("fs_write returned %d, expected %d", written, sizeof(value));
}
RET_IF_ERR(fs_sync(&file));
return fs_close(&file);
}
int prst_debug_counters_get(const char* counter_name, prst_debug_counter_t* value) {
char filename[PRST_MAX_FILE_NAME_LENGTH];
RET_IF_ERR(mk_filename(counter_name, filename, sizeof(filename)));
LOG_DBG("Getting counter %s", filename);
// Open the file.
struct fs_file_t file;
fs_file_t_init(&file);
RET_IF_ERR(fs_open(&file, filename, FS_O_CREATE | FS_O_READ));
// Read the current value.
RET_IF_ERR(read_counter_file(&file, value));
return fs_close(&file);
}
int prst_debug_counters_get_all(prst_debug_counters_callback_t callback) {
LOG_DBG("Getting all counters from %s", kDebugCountersDir);
struct fs_dir_t dirp;
fs_dir_t_init(&dirp);
static struct fs_dirent entry;
RET_IF_ERR(fs_opendir(&dirp, kDebugCountersDir));
prst_debug_counter_t value;
for (;;) {
RET_IF_ERR(fs_readdir(&dirp, &entry));
// End of directory;
if (entry.name[0] == 0) {
break;
}
LOG_DBG("Found %s", entry.name);
prst_debug_counters_get(entry.name, &value);
callback(entry.name, value);
}
return fs_closedir(&dirp);
}

View file

@ -0,0 +1,18 @@
#ifndef _PRST_DEBUG_COUNTERS_H_
#define _PRST_DEBUG_COUNTERS_H_
#include <stdint.h>
typedef uint32_t prst_debug_counter_t;
typedef void (*prst_debug_counters_callback_t)(const char* counter_name, prst_debug_counter_t value);
int prst_debug_counters_init();
int prst_debug_counters_increment(const char* counter_name);
int prst_debug_counters_get(const char* counter_name, prst_debug_counter_t* value);
int prst_debug_counters_get_all(prst_debug_counters_callback_t callback);
#endif // _PRST_DEBUG_COUNTERS_H_

View file

@ -7,17 +7,10 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include "debug_counters.h"
LOG_MODULE_REGISTER(double_reset_detector, CONFIG_LOG_DEFAULT_LEVEL);
FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
static struct fs_mount_t lfs_storage_mnt = {
.type = FS_LITTLEFS,
.fs_data = &storage,
.storage_dev = (void *)FLASH_AREA_ID(storage),
.mnt_point = "/lfs",
};
static const char *flag_filename = "/lfs/reset_flag";
static const char flag_prefix[] = "prst-rst-count";
@ -38,12 +31,6 @@ void erase_flag_callback(struct k_work *work) {
K_WORK_DELAYABLE_DEFINE(erase_flag_work, erase_flag_callback);
int prst_detect_double_reset(prst_double_reset_callback_t on_double_reset) {
// TODO: if booting for the first time after a full flash erase, fs_mount will
// complain (via a LOG_ERR) and then automatically format the flash. It all works,
// but avoiding a scary red message would be ideal. Maybe somehow check if it's
// formatted before mounting?
RET_IF_ERR(fs_mount(&lfs_storage_mnt));
fs_file_t_init(&flag_file);
RET_IF_ERR(fs_open(&flag_file, flag_filename, FS_O_CREATE | FS_O_RDWR));
@ -55,6 +42,7 @@ int prst_detect_double_reset(prst_double_reset_callback_t on_double_reset) {
if (strcmp(buff, flag_prefix) == 0) {
RET_IF_ERR(fs_close(&flag_file));
RET_IF_ERR(erase_flag());
prst_debug_counters_increment("double_reset");
return on_double_reset();
}
@ -64,7 +52,7 @@ int prst_detect_double_reset(prst_double_reset_callback_t on_double_reset) {
// Write the flag and erase it after some time.
ssize_t written = fs_write(&flag_file, flag_prefix, sizeof(flag_prefix));
if (written != sizeof(flag_prefix)) {
LOG_ERR("s_write returned %d, expected %d", written, sizeof(flag_prefix));
LOG_ERR("fs_write returned %d, expected %d", written, sizeof(flag_prefix));
return -1;
}

View file

@ -1,6 +1,7 @@
#include "factory_reset.h"
#include <hal/nrf_power.h>
#include <prstlib/button.h>
#include <prstlib/led.h>
#include <zephyr/logging/log.h>
#include <zigbee/zigbee_app_utils.h>
@ -10,8 +11,8 @@
LOG_MODULE_REGISTER(factory_reset, CONFIG_LOG_DEFAULT_LEVEL);
static int factory_reset() {
// TODO: consider zb_bdb_reset_via_local_action(/*param=*/0);
zigbee_erase_persistent_storage(/*erase=*/true);
LOG_WRN("Factory resetting device");
zb_bdb_reset_via_local_action(/*param=*/0);
return 0;
}
@ -37,10 +38,38 @@ static int factory_reset_if_reset_via_reset_pin() {
}
#endif // CONFIG_PRST_ZB_FACTORY_RESET_VIA_RESET_PIN
#if CONFIG_PRST_ZB_FACTORY_RESET_VIA_SW1
static void timer_do_reset(zb_uint8_t unused_param) {
LOG_WRN("SW1 button was pressed for 5 seconds, factory resetting device");
prst_led_flash(/*times=*/5);
factory_reset();
}
static void sw1_factory_reset_check_timer_cb(struct k_timer *timer_id) {
if (!prst_button_poll(PRST_BUTTON_SW1)) {
LOG_DBG("SW1 button was released, will not factory reset device");
return;
}
ZB_SCHEDULE_APP_CALLBACK(timer_do_reset, /*param=*/0);
}
K_TIMER_DEFINE(sw1_factory_reset_check_timer, sw1_factory_reset_check_timer_cb, NULL);
#endif
int prst_zb_factory_reset_check() {
#if CONFIG_PRST_ZB_FACTORY_RESET_VIA_DOUBLE_RESET
return prst_detect_double_reset(double_reset_handler);
#elif CONFIG_PRST_ZB_FACTORY_RESET_VIA_RESET_PIN
return factory_reset_if_reset_via_reset_pin();
#elif CONFIG_PRST_ZB_FACTORY_RESET_VIA_SW1
if (prst_button_poll(PRST_BUTTON_SW1)) {
LOG_DBG("SW1 pressed. Scheduling timer");
k_timer_start(&sw1_factory_reset_check_timer, K_SECONDS(5), K_NO_WAIT);
}
return 0;
#elif CONFIG_PRST_ZB_FACTORY_RESET_DISABLED
return 0;
#else
#error "No factory reset method selected -- explicitly select CONFIG_PRST_ZB_FACTORY_RESET_DISABLED=y to disable it"
#endif // CONFIG_PRST_ZB_FACTORY_RESET_VIA_RESET_PIN
}

View file

@ -0,0 +1,26 @@
#include "flash_fs.h"
#include <prstlib/macros.h>
#include <zephyr/fs/fs.h>
#include <zephyr/fs/littlefs.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(flash_fs, CONFIG_LOG_DEFAULT_LEVEL);
FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
static struct fs_mount_t lfs_storage_mnt = {
.type = FS_LITTLEFS,
.fs_data = &storage,
.storage_dev = (void *)FIXED_PARTITION_ID(storage_partition),
.mnt_point = "/lfs",
};
int prst_flash_fs_init() {
// TODO: if booting for the first time after a full flash erase, fs_mount will
// complain (via a LOG_ERR) and then automatically format the flash. It all works,
// but avoiding a scary red message would be ideal. Maybe somehow check if it's
// formatted before mounting?
RET_IF_ERR(fs_mount(&lfs_storage_mnt));
return 0;
}

View file

@ -0,0 +1,9 @@
#ifndef _PRST_FLASH_FS_H_
#define _PRST_FLASH_FS_H_
#include <stddef.h>
// Initializes and mounts littlefs.
int prst_flash_fs_init();
#endif // _PRST_FLASH_FS_H_

View file

@ -10,16 +10,20 @@
#include <zboss_api.h>
#include <zboss_api_addons.h>
#include <zcl/zb_zcl_power_config.h>
#include <zephyr/drivers/hwinfo.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zigbee/zigbee_app_utils.h>
#include <zigbee/zigbee_error_handler.h>
#include "debug_counters.h"
#include "factory_reset.h"
#include "flash_fs.h"
#include "prst_zb_attrs.h"
#include "prst_zb_endpoint_defs.h"
#include "prst_zb_soil_moisture_defs.h"
#include "restart_handler.h"
#include "watchdog.h"
LOG_MODULE_REGISTER(app, CONFIG_LOG_DEFAULT_LEVEL);
@ -120,84 +124,28 @@ void identify_cb(zb_bufid_t bufid) {
prst_led_flash(15);
}
void zboss_signal_handler(zb_bufid_t bufid) {
// See zigbee_default_signal_handler() for all available signals.
zb_zdo_app_signal_hdr_t *sig_hndler = NULL;
zb_zdo_app_signal_type_t sig = zb_get_app_signal(bufid, /*sg_p=*/&sig_hndler);
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);
if (status == RET_OK) {
LOG_DBG("Steering successful. Status: %d", status);
prst_led_flash(/*times=*/3);
k_timer_stop(&led_flashing_timer);
prst_restart_watchdog_stop();
prst_led_off();
} else {
LOG_DBG("Steering failed. Status: %d", status);
prst_led_flash(7);
prst_restart_watchdog_start();
k_timer_stop(&led_flashing_timer); // Power saving
prst_led_off();
}
}
case ZB_BDB_SIGNAL_DEVICE_FIRST_START:
joining_signal_received = true;
break;
case ZB_ZDO_SIGNAL_LEAVE:
if (status == RET_OK) {
k_timer_start(&led_flashing_timer, K_NO_WAIT, K_SECONDS(1));
zb_zdo_signal_leave_params_t *leave_params = ZB_ZDO_SIGNAL_GET_PARAMS(sig_hndler, zb_zdo_signal_leave_params_t);
LOG_INF("Network left (leave type: %d)", leave_params->leave_type);
/* Set joining_signal_received to false so broken rejoin procedure can be detected correctly. */
if (leave_params->leave_type == ZB_NWK_LEAVE_TYPE_REJOIN) {
joining_signal_received = false;
}
}
case ZB_ZDO_SIGNAL_SKIP_STARTUP: {
stack_initialised = true;
LOG_DBG("Started zigbee stack and waiting for connection to network.");
k_timer_start(&led_flashing_timer, K_NO_WAIT, K_SECONDS(1));
break;
}
case ZB_NLME_STATUS_INDICATION: {
zb_zdo_signal_nlme_status_indication_params_t *nlme_status_ind =
ZB_ZDO_SIGNAL_GET_PARAMS(sig_hndler, zb_zdo_signal_nlme_status_indication_params_t);
if (nlme_status_ind->nlme_status.status == ZB_NWK_COMMAND_STATUS_PARENT_LINK_FAILURE) {
/* Check for broken rejoin procedure and restart the device to recover.
This implements Nordic's suggested workaround for errata KRKNWK-12017, which effects
the recent nRF Connect SDK (v1.8.0 - v2.3.0 at time of writing).
For details see: https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/known_issues.html?v=v2-3-0
*/
if (stack_initialised && !joining_signal_received) {
zb_reset(0);
}
}
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");
LOG_DBG("Updating sensors");
ZB_SCHEDULE_APP_ALARM(update_sensors_cb,
/*param=*/0,
ZB_TIME_ONE_SECOND * CONFIG_PRST_ZB_SLEEP_DURATION_SEC);
if (prst_sensors_read_all(&sensors)) {
LOG_ERR("Unable to read sensors");
return;
// Reschedule the same callback.
zb_ret_t ret = ZB_SCHEDULE_APP_ALARM(
update_sensors_cb,
/*param=*/0,
ZB_MILLISECONDS_TO_BEACON_INTERVAL(1000 * CONFIG_PRST_ZB_SLEEP_DURATION_SEC));
if (ret != RET_OK) {
prst_debug_counters_increment("sens_cb_schedule_err");
__ASSERT(false, "Unable to schedule sensor update callback");
}
__ASSERT(!prst_watchdog_feed(), "Failed to feed watchdog");
prst_debug_counters_increment("sensors_read_before");
if (prst_sensors_read_all(&sensors)) {
prst_debug_counters_increment("sensors_read_error");
__ASSERT(false, "Unable to read sensors");
}
prst_debug_counters_increment("sensors_read_after");
// 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,
@ -235,30 +183,149 @@ void update_sensors_cb(zb_uint8_t arg) {
&log_lux);
}
void zboss_signal_handler(zb_bufid_t bufid) {
// See zigbee_default_signal_handler() for all available signals.
zb_zdo_app_signal_hdr_t *sig_hndler = NULL;
zb_zdo_app_signal_type_t sig = zb_get_app_signal(bufid, /*sg_p=*/&sig_hndler);
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);
if (status == RET_OK) {
LOG_DBG("Steering successful. Status: %d", status);
k_timer_stop(&led_flashing_timer);
prst_led_off();
prst_restart_watchdog_stop();
// Update the long polling parent interval - needs to be done after joining.
zb_zdo_pim_set_long_poll_interval(1000 * CONFIG_PRST_ZB_PARENT_POLL_INTERVAL_SEC);
} else {
LOG_DBG("Steering failed. Status: %d", status);
prst_restart_watchdog_start();
// Power saving.
k_timer_stop(&led_flashing_timer);
prst_led_off();
}
joining_signal_received = true;
break;
}
case ZB_BDB_SIGNAL_DEVICE_FIRST_START:
joining_signal_received = true;
break;
case ZB_ZDO_SIGNAL_LEAVE:
if (status == RET_OK) {
k_timer_start(&led_flashing_timer, K_NO_WAIT, K_SECONDS(1));
zb_zdo_signal_leave_params_t *leave_params = ZB_ZDO_SIGNAL_GET_PARAMS(sig_hndler, zb_zdo_signal_leave_params_t);
LOG_DBG("Network left (leave type: %d)", leave_params->leave_type);
/* Set joining_signal_received to false so broken rejoin procedure can be detected correctly. */
if (leave_params->leave_type == ZB_NWK_LEAVE_TYPE_REJOIN) {
joining_signal_received = false;
}
}
break;
case ZB_ZDO_SIGNAL_SKIP_STARTUP: {
stack_initialised = true;
LOG_DBG("Started zigbee stack and waiting for connection to network.");
k_timer_start(&led_flashing_timer, K_NO_WAIT, K_SECONDS(1));
// Kick off main sensor update task.
ZB_ERROR_CHECK(ZB_SCHEDULE_APP_ALARM(update_sensors_cb,
/*param=*/0,
ZB_MILLISECONDS_TO_BEACON_INTERVAL(1000)));
__ASSERT_NO_MSG(!prst_watchdog_start());
break;
}
case ZB_NLME_STATUS_INDICATION: {
zb_zdo_signal_nlme_status_indication_params_t *nlme_status_ind =
ZB_ZDO_SIGNAL_GET_PARAMS(sig_hndler, zb_zdo_signal_nlme_status_indication_params_t);
if (nlme_status_ind->nlme_status.status == ZB_NWK_COMMAND_STATUS_PARENT_LINK_FAILURE) {
/* Check for broken rejoin procedure and restart the device to recover.
This implements Nordic's suggested workaround for errata KRKNWK-12017, which effects
the recent nRF Connect SDK (v1.8.0 - v2.3.0 at time of writing).
For details see: https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/known_issues.html?v=v2-3-0
*/
if (stack_initialised && !joining_signal_received) {
zb_reset(0);
}
}
break;
}
}
ZB_ERROR_CHECK(zigbee_default_signal_handler(bufid));
if (bufid) {
zb_buf_free(bufid);
}
}
void dump_counter(const char *counter_name, prst_debug_counter_t value) {
LOG_INF("- %s: %d", counter_name, value);
}
int log_reset_reason_counter() {
uint32_t cause;
const char *reset_counter_str = "reset_cause_unknown";
RET_IF_ERR(hwinfo_get_reset_cause(&cause));
RET_IF_ERR(hwinfo_clear_reset_cause());
if (cause & RESET_PIN) {
reset_counter_str = "reset_cause_pin";
} else if (cause & RESET_SOFTWARE) {
// Includes fatal errors from __ASSERT, ZB_ERROR_CHECK and friends.
reset_counter_str = "reset_cause_software";
} else if (cause & RESET_BROWNOUT) {
reset_counter_str = "reset_cause_brownout";
} else if (cause & RESET_POR) {
reset_counter_str = "reset_cause_power_on";
} else if (cause & RESET_WATCHDOG) {
reset_counter_str = "reset_cause_watchdog";
} else if (cause & RESET_DEBUG) {
reset_counter_str = "reset_cause_debug";
} else if (cause & RESET_LOW_POWER_WAKE) {
reset_counter_str = "reset_cause_low_power";
} else if (cause & RESET_CPU_LOCKUP) {
reset_counter_str = "reset_cause_cpu_lockup";
} else if (cause & RESET_HARDWARE) {
reset_counter_str = "reset_cause_hardware";
} else if (cause & RESET_USER) {
reset_counter_str = "reset_cause_user";
}
return prst_debug_counters_increment(reset_counter_str);
}
int main(void) {
RET_IF_ERR(prst_adc_init());
RET_IF_ERR(prst_led_init());
RET_IF_ERR(prst_button_init());
RET_IF_ERR(prst_flash_fs_init());
RET_IF_ERR(prst_debug_counters_init());
RET_IF_ERR(prst_zb_factory_reset_check());
// Initialize sensors - quickly put them into low power mode.
__ASSERT_NO_MSG(!prst_sensors_read_all(&sensors));
prst_debug_counters_increment("boot");
log_reset_reason_counter();
LOG_INF("Dumping debug counters:");
prst_debug_counters_get_all(dump_counter);
__ASSERT_NO_MSG(!prst_zb_factory_reset_check());
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));
prst_led_flash(2);
k_msleep(100);
ZB_AF_SET_IDENTIFY_NOTIFICATION_HANDLER(PRST_ZIGBEE_ENDPOINT, identify_cb);
zigbee_enable();
zigbee_configure_sleepy_behavior(/*enable=*/true);
power_down_unused_ram();
zigbee_enable();
prst_debug_counters_increment("main_finish");
return 0;
}

View file

@ -4,14 +4,25 @@
#include <zephyr/logging/log.h>
#include <zigbee/zigbee_app_utils.h>
#include "debug_counters.h"
LOG_MODULE_REGISTER(restart_handler, CONFIG_LOG_DEFAULT_LEVEL);
static void restart_network_steering_cb(struct k_timer *timer) {
LOG_DBG("Restart handler expired. Restarting network steering.");
void callback_work_handler(struct k_work *work) {
LOG_INF("Running restart callback_work_handler.");
prst_debug_counters_increment("steering_watchdog_restart");
// If the device is not commissioned, the rejoin procedure is started.
user_input_indicate();
}
K_WORK_DEFINE(callback_work, callback_work_handler);
// Runs in an ISR context. We offload the actual work to a workqueue.
static void restart_network_steering_cb(struct k_timer *timer) {
LOG_INF("Triggered restart_network_steering_cb. Offloading work.");
k_work_submit(&callback_work);
}
K_TIMER_DEFINE(restart_timer, restart_network_steering_cb, NULL);
void prst_restart_watchdog_start() {

View file

@ -0,0 +1,47 @@
#include "watchdog.h"
#include <prstlib/macros.h>
#include <zephyr/device.h>
#include <zephyr/drivers/watchdog.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#define PRST_ZB_WATCHDOG_TIMEOUT_SEC (2 * CONFIG_PRST_ZB_SLEEP_DURATION_SEC)
LOG_MODULE_REGISTER(watchdog, CONFIG_LOG_DEFAULT_LEVEL);
typedef struct {
const struct device *const wdt;
int wtd_channel_id;
} wtd_data_t;
static wtd_data_t wtd_data = {
.wdt = DEVICE_DT_GET(DT_NODELABEL(wdt)),
};
int prst_watchdog_start() {
static const struct wdt_timeout_cfg wdt_settings = {
.window = {
.min = 0,
.max = PRST_ZB_WATCHDOG_TIMEOUT_SEC * MSEC_PER_SEC,
},
// NULL callback means use the default, which is the system reset.
.callback = NULL,
.flags = WDT_FLAG_RESET_SOC};
RET_IF_ERR(!device_is_ready(wtd_data.wdt));
// Install timeout.
wtd_data.wtd_channel_id = wdt_install_timeout(wtd_data.wdt, &wdt_settings);
RET_CHECK(wtd_data.wtd_channel_id >= 0, "Failed to install watchdog timeout.");
// Start watchdog.
RET_IF_ERR(wdt_setup(wtd_data.wdt, WDT_OPT_PAUSE_HALTED_BY_DBG));
return 0;
}
int prst_watchdog_feed() {
RET_IF_ERR(wdt_feed(wtd_data.wdt, wtd_data.wtd_channel_id));
return 0;
}

View file

@ -0,0 +1,8 @@
#ifndef _PRST_ZB_WATCHDOG_H_
#define _PRST_ZB_WATCHDOG_H_
int prst_watchdog_start();
int prst_watchdog_feed();
#endif // _PRST_ZB_WATCHDOG_H_

View file

@ -3,7 +3,7 @@ Comment,Designator,Footprint,LCSC
"1n","C1","Capacitor_SMD:C_0402_1005Metric","C1523"
"100n","C2","Capacitor_SMD:C_0402_1005Metric","C1525"
"12p","C3,C4","Capacitor_SMD:C_0402_1005Metric","C1547"
"LL4148","D1","Diode_SMD:D_MiniMELF","C9808"
"LL4148","D1","Diode_SMD:D_MiniMELF","C3011166"
"LED","D2","LED_SMD:LED_0603_1608Metric","C2286"
"MMBT3906","Q1","Package_TO_SOT_SMD:SOT-23","C2910191"
"Q_Photo_NPN","Q4","snapeda:TR8","C146233"

1 Comment Designator Footprint LCSC
3 1n C1 Capacitor_SMD:C_0402_1005Metric C1523
4 100n C2 Capacitor_SMD:C_0402_1005Metric C1525
5 12p C3,C4 Capacitor_SMD:C_0402_1005Metric C1547
6 LL4148 D1 Diode_SMD:D_MiniMELF C9808 C3011166
7 LED D2 LED_SMD:LED_0603_1608Metric C2286
8 MMBT3906 Q1 Package_TO_SOT_SMD:SOT-23 C2910191
9 Q_Photo_NPN Q4 snapeda:TR8 C146233

View file

@ -1027,7 +1027,7 @@
(at 66.742 55.536 180)
(descr "Diode Mini-MELF")
(tags "Diode Mini-MELF")
(property "LCSC" "C9808")
(property "LCSC" "C3011166")
(property "Sheetfile" "parasite.kicad_sch")
(property "Sheetname" "")
(property "ki_description" "100V 0.15A standard switching diode, MiniMELF")

View file

@ -1963,10 +1963,10 @@
(property "Footprint" "Diode_SMD:D_MiniMELF" (at 63.5 111.125 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "http://www.vishay.com/docs/85557/ll4148.pdf" (at 63.5 115.57 0)
(property "Datasheet" "https://datasheet.lcsc.com/lcsc/2205131645_JUXING-LL4148_C3011166.pdf" (at 63.5 115.57 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "LCSC" "C9808" (at 63.5 115.57 0)
(property "LCSC" "C3011166" (at 63.5 115.57 0)
(effects (font (size 1.27 1.27)) hide)
)
(pin "1" (uuid b7ede69c-719d-4a3e-977e-6b4e4c5e39dd))