Compare commits
286 commits
ble-protoc
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5214f904f1 | ||
|
|
20693143ae | ||
|
|
2e5f89b228 | ||
|
|
2dc7144de1 | ||
|
|
5f671b0170 | ||
|
|
21dd14d7f3 | ||
|
|
f8c28cebd6 | ||
|
|
8ef2b5b748 | ||
|
|
4e0f3911bd | ||
|
|
20d98d02a0 | ||
|
|
ccdbaaf142 | ||
|
|
57cf5aa0e5 | ||
|
|
e7e4b77eef | ||
|
|
dc02480da9 | ||
|
|
4796bfab5f | ||
|
|
e62521d2ae | ||
|
|
16c4d23c47 | ||
|
|
15920b1ae4 | ||
|
|
d26eab5825 | ||
|
|
c7aaf5b388 | ||
|
|
905978e8c9 | ||
|
|
4c0fe9b4c6 | ||
|
|
dd373ea235 | ||
|
|
25a5941777 | ||
|
|
37ea3a1062 | ||
|
|
d3ddd089bd | ||
|
|
28c2809b04 | ||
|
|
ad3cd4194f | ||
|
|
171a1eb4a0 | ||
|
|
5199ceaa50 | ||
|
|
7de5115705 | ||
|
|
d5ed701c45 | ||
|
|
141071a414 | ||
|
|
35618227d5 | ||
|
|
77c204397e | ||
|
|
5d2b648b22 | ||
|
|
da1642514a | ||
|
|
85ca1a67bc | ||
|
|
e68c113513 | ||
|
|
e0481994b2 | ||
|
|
9ea5c7b4d0 | ||
|
|
1a5acd02a5 | ||
|
|
8fb45f7165 | ||
|
|
9fdd15626a | ||
|
|
c70eb600a3 | ||
|
|
95ec660cb6 | ||
|
|
d84a6422fe | ||
|
|
8c21fe73d5 | ||
|
|
0a73159ce9 | ||
|
|
105016e24c | ||
|
|
bc3d834b20 | ||
|
|
37aff683ad | ||
|
|
c86f04e406 | ||
|
|
7f42dd0c00 | ||
|
|
46feb542cc | ||
|
|
62a99cc716 | ||
|
|
90f99bd5b0 | ||
|
|
5882312a77 | ||
|
|
a4a02257f9 | ||
|
|
f9f122b818 | ||
|
|
d5891f4a6f | ||
|
|
56c5fa7ba0 | ||
|
|
f6b4600044 | ||
|
|
afa53f0e94 | ||
|
|
426195184d | ||
|
|
c9df52fd5f | ||
|
|
930239e385 | ||
|
|
015ce94bbd | ||
|
|
987c4f52c0 | ||
|
|
509350457f | ||
|
|
bcc5a853d0 | ||
|
|
3aa33cdd89 | ||
|
|
ce748c4759 | ||
|
|
5cede82dc5 | ||
|
|
9285a6bc41 | ||
|
|
6f10dc9e19 | ||
|
|
c52d5f7a48 | ||
|
|
727138415e | ||
|
|
69c0ed3694 | ||
|
|
ba786eb298 | ||
|
|
6580cf40a2 | ||
|
|
54b1c48638 | ||
|
|
e56a7748b0 | ||
|
|
117b257336 | ||
|
|
70d716520d | ||
|
|
414427df5b | ||
|
|
fbf8e09f0d | ||
|
|
0ca44f737b | ||
|
|
7ac6d98b3b | ||
|
|
9ff5180cd7 | ||
|
|
fdf4609131 | ||
|
|
00d11f50a7 | ||
|
|
caec746c26 | ||
|
|
cb40173b6b | ||
|
|
ebc26fcccd | ||
|
|
dbded329a4 | ||
|
|
e82c1b6bc1 | ||
|
|
2c052cd376 | ||
|
|
20d6c893bf | ||
|
|
02b3970ffc | ||
|
|
6c618d946c | ||
|
|
ead6fd434d | ||
|
|
bf9f4e1b5b | ||
|
|
a6995d1ce8 | ||
|
|
c7128ef163 | ||
|
|
5eef25f86b | ||
|
|
19e0354113 | ||
|
|
5cc40a41f6 | ||
|
|
7f652ce75d | ||
|
|
b34bc60a0a | ||
|
|
2f7a25e266 | ||
|
|
e24530b7c3 | ||
|
|
1983f31af9 | ||
|
|
dbf288138e | ||
|
|
53f806a8e5 | ||
|
|
cfc5491849 | ||
|
|
8dceb6c7cc | ||
|
|
a79c1579f3 | ||
|
|
e71eaf9823 | ||
|
|
df6e327947 | ||
|
|
b3ae950ee0 | ||
|
|
1a2dcbb70b | ||
|
|
03a281fada | ||
|
|
123149d3d2 | ||
|
|
d5a548a110 | ||
|
|
1ffc876ad0 | ||
|
|
34ddee5ea3 | ||
|
|
3959abbf03 | ||
|
|
2bff7d8741 | ||
|
|
abe8845c4b | ||
|
|
40a8676e59 | ||
|
|
5e315ba9cd | ||
|
|
7be5fe1669 | ||
|
|
d292190d31 | ||
|
|
d3bcc13749 | ||
|
|
4b277bd62f | ||
|
|
ee708db400 | ||
|
|
05e25d166a | ||
|
|
997bfb7518 | ||
|
|
28db7224a6 | ||
|
|
41ba531fba | ||
|
|
424b471eb8 | ||
|
|
af47848b80 | ||
|
|
119055b97a | ||
|
|
ee38d4561a | ||
|
|
1fbd971c4f | ||
|
|
5d25499ae1 | ||
|
|
8105bfcd7b | ||
|
|
98433502af | ||
|
|
14399dffb4 | ||
|
|
54792ce35f | ||
|
|
c5a129a39a | ||
|
|
3d68e5c689 | ||
|
|
a34b1f8721 | ||
|
|
c76b8dc289 | ||
|
|
fe63fda2f1 | ||
|
|
c567408861 | ||
|
|
06627ff85d | ||
|
|
54382e3356 | ||
|
|
0f859c77cd | ||
|
|
580f425b01 | ||
|
|
ac8677f3e8 | ||
|
|
0933c4a2a6 | ||
|
|
412188502b | ||
|
|
891a63a1b3 | ||
|
|
61729aa81a | ||
|
|
3b91d7525c | ||
|
|
0a3d711787 | ||
|
|
03b45062c0 | ||
|
|
96e1e85179 | ||
|
|
228b6f2f16 | ||
|
|
5da4ad836f | ||
|
|
37237a4301 | ||
|
|
17b487f362 | ||
|
|
3a6561bbf4 | ||
|
|
7b4e64cec7 | ||
|
|
8eb54106d5 | ||
|
|
3c2094db2d | ||
|
|
fbf1a35099 | ||
|
|
cb6ed73691 | ||
|
|
676b525bd9 | ||
|
|
01bb9da2a4 | ||
|
|
f7373cc6da | ||
|
|
15b1d9d2a8 | ||
|
|
9b1efb7f82 | ||
|
|
d857448df8 | ||
|
|
55808ed7e1 | ||
|
|
d8322c9162 | ||
|
|
f090b25a2c | ||
|
|
f05d1f8e3b | ||
|
|
af3a9add74 | ||
|
|
fdf41575a7 | ||
|
|
3bf90451db | ||
|
|
192cca127c | ||
|
|
002500dcd6 | ||
|
|
a8c7d3c1cd | ||
|
|
f7806d4c7e | ||
|
|
cab55d9b8c | ||
|
|
381bbb7ceb | ||
|
|
cc62786d16 | ||
|
|
8bfd75de78 | ||
|
|
bc08c8284a | ||
|
|
7c0b83e94e | ||
|
|
6969d74a8c | ||
|
|
f476e69229 | ||
|
|
dc88916823 | ||
|
|
a93171b9bf | ||
|
|
b483af4126 | ||
|
|
ad45efb6d9 | ||
|
|
b731006dbb | ||
|
|
a9a478f68f | ||
|
|
0bda7de7cf | ||
|
|
fe09c0b2f1 | ||
|
|
9c62b12446 | ||
|
|
dbaf44ecc1 | ||
|
|
3b104cdf24 | ||
|
|
4a6e3dcec3 | ||
|
|
fc3a0652aa | ||
|
|
46a8285f59 | ||
|
|
89d2999bd8 | ||
|
|
cc4c47927b | ||
|
|
00a7114dd1 | ||
|
|
5ceccfafcc | ||
|
|
df7700cdd7 | ||
|
|
a9d551f33c | ||
|
|
5510fd963c | ||
|
|
339e5bebe9 | ||
|
|
6705c11867 | ||
|
|
d18e7ab2d6 | ||
|
|
c5a5fcaf5f | ||
|
|
a65b00a439 | ||
|
|
9333913571 | ||
|
|
8c7a2c35de | ||
|
|
597542649a | ||
|
|
3749d689ba | ||
|
|
8cfb87ef32 | ||
|
|
ffeca36c27 | ||
|
|
c15120178f | ||
|
|
a639343b6e | ||
|
|
427f9279de | ||
|
|
2779fe9af6 | ||
|
|
3d6b4eaef1 | ||
|
|
59f2aad3db | ||
|
|
107936770e | ||
|
|
6b1f52925e | ||
|
|
522452b465 | ||
|
|
9c9492567f | ||
|
|
bae06952fb | ||
|
|
baec1c2a80 | ||
|
|
fb7dff8bfe | ||
|
|
54c6c09ff0 | ||
|
|
149e15480d | ||
|
|
d8dfcc69a9 | ||
|
|
e45bb43b84 | ||
|
|
100f4046fa | ||
|
|
53e52f3267 | ||
|
|
a25ec5ca58 | ||
|
|
8202761646 | ||
|
|
39f839b794 | ||
|
|
1174bcab67 | ||
|
|
f4df95e6a0 | ||
|
|
785c8d7874 | ||
|
|
e1ec8fd5a8 | ||
|
|
220ff6ec61 | ||
|
|
0c7222d199 | ||
|
|
9c2c3d7c3a | ||
|
|
f44e91f865 | ||
|
|
b0cbba00c4 | ||
|
|
c339538913 | ||
|
|
314a7f2fe3 | ||
|
|
7ebfe7e399 | ||
|
|
82eabe778a | ||
|
|
5c1ea4a164 | ||
|
|
81f0af3cf2 | ||
|
|
ce4c496f4d | ||
|
|
6ea0bb8525 | ||
|
|
cc8dcf9240 | ||
|
|
5cfd69479a | ||
|
|
a7732f106b | ||
|
|
0e9b9feb97 | ||
|
|
d56f60ceef | ||
|
|
e7f7f96797 | ||
|
|
6382167efb | ||
|
|
8b80fe6db0 | ||
|
|
e047929617 | ||
|
|
59d28c729a |
198 changed files with 203801 additions and 43430 deletions
13
.devcontainer/devcontainer.json
Normal file
13
.devcontainer/devcontainer.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "nrf-connect:v2.4",
|
||||
"image": "nordicplayground/nrfconnect-sdk:v2.5-branch",
|
||||
"features": {
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"nordic-semiconductor.nrf-connect"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
13
.github/actions/build/Dockerfile
vendored
13
.github/actions/build/Dockerfile
vendored
|
|
@ -1,13 +0,0 @@
|
|||
FROM debian:bullseye-slim
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get -y install wget tar unzip make clang-format gcc-arm-none-eabi
|
||||
|
||||
RUN cd /opt && \
|
||||
wget https://www.nordicsemi.com/-/media/Software-and-other-downloads/SDKs/nRF5/Binaries/nRF5SDK1702d674dde.zip -O nRF5_SDK.zip && \
|
||||
unzip nRF5_SDK.zip && \
|
||||
mv nRF5_SDK_17.0.2_d674dde nRF5_SDK
|
||||
|
||||
COPY build.sh /build.sh
|
||||
|
||||
ENTRYPOINT ["/build.sh"]
|
||||
30
.github/actions/build/action.yml
vendored
30
.github/actions/build/action.yml
vendored
|
|
@ -1,5 +1,27 @@
|
|||
name: 'Build'
|
||||
description: 'Builds b-parasite firmware'
|
||||
name: "Build"
|
||||
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"
|
||||
default: bparasite_nrf52840
|
||||
revision:
|
||||
description: "Board revision use"
|
||||
default: "2.0.0"
|
||||
cmake-extra:
|
||||
description: "Extra CMake arguments"
|
||||
default: ""
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
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
|
||||
|
|
|
|||
10
.github/actions/build/build.sh
vendored
10
.github/actions/build/build.sh
vendored
|
|
@ -1,10 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -eux -o pipefail
|
||||
|
||||
export SDK_ROOT=/opt/nRF5_SDK
|
||||
export GNU_INSTALL_ROOT=/usr/bin/
|
||||
|
||||
cd "$GITHUB_WORKSPACE/code/b-parasite"
|
||||
make clean
|
||||
make lint
|
||||
make
|
||||
100
.github/workflows/b-parasite.yml
vendored
100
.github/workflows/b-parasite.yml
vendored
|
|
@ -6,12 +6,106 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
name: Checks format & builds b-parasite's firmware
|
||||
name: Check code format
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Check clang-format
|
||||
uses: jidicula/clang-format-action@v4.9.0
|
||||
with:
|
||||
check-path: "code/nrf-connect"
|
||||
exclude-regex: '\/build\/'
|
||||
|
||||
build-blinky:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build blinky
|
||||
needs:
|
||||
- lint
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build blinky
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
sample-dir: code/nrf-connect/samples/blinky
|
||||
board: bparasite_nrf52840
|
||||
revision: 2.0.0
|
||||
output-bin: blinky_nrf52840.hex
|
||||
|
||||
build-input:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build input
|
||||
needs:
|
||||
- lint
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build input
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
sample-dir: code/nrf-connect/samples/input
|
||||
board: bparasite_nrf52840
|
||||
revision: 2.0.0
|
||||
output-bin: input_nrf52840.hex
|
||||
|
||||
build-soil-read-loop:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build soil_read_loop
|
||||
needs:
|
||||
- lint
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build soil_read_loop
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
sample-dir: code/nrf-connect/samples/soil_read_loop
|
||||
board: bparasite_nrf52840
|
||||
revision: 2.0.0
|
||||
output-bin: soil_read_loop_nrf52840.hex
|
||||
|
||||
build-ble:
|
||||
strategy:
|
||||
matrix:
|
||||
soc: [nrf52840, nrf52833]
|
||||
revision: [1.1.0, 1.2.0, 2.0.0]
|
||||
runs-on: ubuntu-latest
|
||||
name: Build ble ${{ matrix.soc }}@${{ matrix.revision }}
|
||||
needs:
|
||||
- lint
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build
|
||||
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_MSEC=1000 -DCONFIG_PRSTLIB_LOG_LEVEL_DBG=y
|
||||
output-bin: ble_${{ matrix.soc }}_${{ matrix.revision }}_debug.hex
|
||||
|
||||
build-zigbee:
|
||||
strategy:
|
||||
matrix:
|
||||
soc: [nrf52840, nrf52833]
|
||||
revision: [1.2.0, 2.0.0]
|
||||
runs-on: ubuntu-latest
|
||||
name: Build zigbee ${{ matrix.soc }}@${{ matrix.revision }}
|
||||
needs:
|
||||
- lint
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Build
|
||||
uses: ./.github/actions/build
|
||||
with:
|
||||
sample-dir: code/nrf-connect/samples/zigbee
|
||||
board: bparasite_${{ matrix.soc }}
|
||||
revision: ${{ matrix.revision }}
|
||||
cmake-extra: -DCONFIG_PRSTLIB_LOG_LEVEL_DBG=y
|
||||
output-bin: zigbee_${{ matrix.soc }}_${{ matrix.revision }}_debug.hex
|
||||
|
|
|
|||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
rbaron.net/contact.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
92
README.md
92
README.md
|
|
@ -2,81 +2,59 @@
|
|||
|
||||
# b-parasite
|
||||
<p align="center">
|
||||
<img src="img/resized/img1.jpg" width="512px" border="0" alt="PCB front and back photo" />
|
||||
<img src="img/resized/b-parasite-2.0.0.jpg" width="512px" border="0" alt="PCB front and back photo" />
|
||||
</p>
|
||||
|
||||
b-parasite is an open source Bluetooth Low Energy (BLE) soil moisture and ambient temperature/humidity/light sensor.
|
||||
b-parasite is an open source soil moisture and ambient temperature/humidity/light sensor.
|
||||
|
||||
# Features
|
||||
* Soil moisture sensor. I wrote about how capacitive soil moisture sensors works on [this Twitter thread](https://twitter.com/rbaron_/status/1367182806368071685), based on [this great post](https://wemakethings.net/2012/09/26/capacitance_measurement/) on wemakethings.net
|
||||
* Air temperature and humity 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
|
||||
* Powered by a common CR2032 coin cell, with a battery life of possibly over a year - see "Battery Life" below
|
||||
* 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://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
|
||||
|
||||
# Wiki Pages
|
||||
* [Hardware Versions](https://github.com/rbaron/b-parasite/wiki/Hardware-Versions)
|
||||
* [How to order: PCB fabrication and SMT assembly](https://github.com/rbaron/b-parasite/wiki/How-to-order:-PCB-fabrication-and-SMT-assembly)
|
||||
* [How to Program](https://github.com/rbaron/b-parasite/wiki/How-to-Program)
|
||||
# Software
|
||||
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/) + [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. |-|
|
||||
|[samples/input](./code/nrf-connect/samples/input)| Handles button presses. Useful for power profiling GPIO interrupts and testing debouncing for push switches on [boards that have them](https://github.com/rbaron/b-parasite/wiki/Hardware-Versions). |-|
|
||||
|
||||
# Documentation
|
||||
Information about how to order, assemble, build the samples, protect the sensor and flash the firmware is on [the Wiki](https://github.com/rbaron/b-parasite/wiki).
|
||||
|
||||
# Repository Organization
|
||||
* [code/b-parasite/](./code/b-parasite/) - firmware code based on the [nRF5 SDK](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fstruct_sdk%2Fstruct%2Fsdk_nrf5_latest.html&cp=7_1)
|
||||
* [code/nrf-connect/](./code/nrf-connect/) - Common library and samples, built with Nordic's [nRF Connect SDK](https://www.nordicsemi.com/Products/Development-software/nrf-connect-sdk).
|
||||
* [kicad/](./kicad/) - KiCad schematic, layout and fabrication files for the printed circuit board (PCB)
|
||||
* [data/](data/) - data for testing and sensor calibration
|
||||
* [bridge/](bridge/) - an [ESPHome](https://github.com/esphome/esphome)-based BLE-MQTT bridge
|
||||
* [case/](case/) - a 3D printable case
|
||||
|
||||
# How It Works
|
||||
<p align="center">
|
||||
<img src="img/excalidraw/diagram.png" border="0" alt="Diagram containing two b-parasites, a bridge & an MQTT broker" />
|
||||
</p>
|
||||
|
||||
b-parasite works by periodically measuring the soil moisture, air temperature/humidity and broadcasting those values via Bluetooth Low Energy (BLE) advertisement packets. After doing so, the board goes into a sleep mode until it's time for another measurement. The sleep interval is configurable - I often use 10 minutes between readings, which is a good compromise between fresh data and saving battery.
|
||||
|
||||
At this point, b-parasite's job is done. We have many possibilities of how to capture its BLE advertisement packet and what to do with the data. A common pattern is having a BLE-MQTT bridge that listens for these BLE broadcasts, decodes them and ships the sensor values through MQTT messages. The MQTT broker is then responsible for relaying the sensor data to interested parties. This is the topology shown in the diagram above.
|
||||
|
||||
## Integrations
|
||||
### ESPHome
|
||||
A popular choice for a BLE-MQTT bridge is the [ESPHome](https://github.com/esphome/esphome) project, which runs on our beloved [ESP32](https://www.espressif.com/en/products/socs/esp32) boards. b-parasite is officially supported and documentation for using it can be found in [the b-parasite ESPHome docs](https://esphome.io/components/sensor/b_parasite.html). An example of using this platform is also available in this repo, under [bridge/](bridge/) (check out [README.md](bridge/README.md) there for more info).
|
||||
|
||||
ESPHome is a battle-tested project with a vibrant community, and is currently the most mature b-parasite bridge. ESP32 are also cheap, so you can sprinkle a few of them around the house to cover a wide range, and even share the same ESP32 with other sensors.
|
||||
|
||||
### Home Assistant
|
||||
b-parasite is supported by the [ble_monitor](https://github.com/custom-components/ble_monitor) Home Assistant custom component - please refer to the [docs](https://custom-components.github.io/ble_monitor/by_brand#rbaron). This custom component gets Home Assistant to automatically discover nearby b-parasites based on their advertisement data.
|
||||
|
||||
### Linux/Raspberry Pi & macOS
|
||||
Another possibility is running [parasite-scanner](https://github.com/rbaron/parasite-scanner). It is a purpose-built bridge for b-parasites, and runs on Linux and macOS.
|
||||
|
||||
This is the quickest way to collect and visualize data from b-parasites, and I personally use it a lot for testing and debugging.
|
||||
|
||||
## Protocol and Data Encoding
|
||||
Sensor data is transmitted via BLE advertisement broadcasts. [Here](./code/b-parasite/README.md) you can find a byte-by-byte description of the data as it is encoded inside the advertisement packet.
|
||||
|
||||
# Battery Life
|
||||
**tl;dr:** By taking readings 10 minutes apart, the battery should last for over a year.
|
||||
|
||||
The main parameters involved in estimating the battery life are:
|
||||
* Current consumption (both in operation and during sleep)
|
||||
* Duty cycle (how much time it spends in operation vs. sleeping)
|
||||
* Battery capacity - this is roughly 230 mAh for CR2032 cells
|
||||
|
||||
In the following screenshot, I measured the voltage of a 10 Ohm series resistor during the on-cycle, for a 8dBm transmitting power (the voltage is negative, so it is upside down):
|
||||
|
||||
<p align="center">
|
||||
<img src="img/scope/8dbm-across-10ohm.png" border="0" alt="A screenshot of an oscilloscope used to measure current consumption" />
|
||||
</p>
|
||||
|
||||
The short high peaks correspond to when the radio is active, sending broadcasting packets. The average current consumption during this active time is roughly 9mA. Let's round it to 10mA. During off time, I measure a current of less than 3uA.
|
||||
|
||||
With these parameters in hand, I put together [this spreadsheet](https://docs.google.com/spreadsheets/d/157JQiX20bGkTrlbvWbWRrs_WViL3MgVZffSCWRR7uAI/edit#gid=0) in which you can estimate the battery life. For example, for an active time of one second and sleep time of ten minutes, we see a runtime of 488.10 days.
|
||||
|
||||
<p align="center">
|
||||
<img src="img/resized/img2.jpg" border="0" alt="b-parasite stuck into a small plant vase" />
|
||||
</p>
|
||||
|
||||
# Case
|
||||
A 3D printable case model can be found in [case/](case/).
|
||||

|
||||
<p align="center">
|
||||
<img src="img/case/screenshot.png" alt="Render of the original 3D printable case" />
|
||||
</p>
|
||||
|
||||
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/).
|
||||
|
|
|
|||
31273
case/Case.step
31273
case/Case.step
File diff suppressed because it is too large
Load diff
BIN
case/Top-holes.stl
Normal file
BIN
case/Top-holes.stl
Normal file
Binary file not shown.
BIN
case/b_parasite_case_high_airflow.3mf
Normal file
BIN
case/b_parasite_case_high_airflow.3mf
Normal file
Binary file not shown.
BIN
case/b_parasite_case_high_airflow.stl
Normal file
BIN
case/b_parasite_case_high_airflow.stl
Normal file
Binary file not shown.
4
code/.clang-format
Normal file
4
code/.clang-format
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 0
|
||||
1
code/b-parasite/.gitignore
vendored
1
code/b-parasite/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
_build
|
||||
|
|
@ -1,337 +0,0 @@
|
|||
# Supported platforms:
|
||||
# * E73_2G4M08S1C (nrf2480, default)
|
||||
# * E73_2G4M08S1E (nrf2833)
|
||||
# note: the missing whitespace after the = is on purpose here
|
||||
PLATFORM ?=E73_2G4M08S1C
|
||||
|
||||
ifeq ($(PLATFORM),E73_2G4M08S1E)
|
||||
SRC_FILES := \
|
||||
$(SDK_ROOT)/modules/nrfx/mdk/gcc_startup_nrf52833.S \
|
||||
$(SDK_ROOT)/modules/nrfx/mdk/system_nrf52833.c
|
||||
TARGETS := nrf52833_xxaa
|
||||
CFLAGS += -DNRF52833_XXAA
|
||||
ASMFLAGS += -DNRF52833_XXAA
|
||||
LINKER_SCRIPT := b_parasite_gcc_nrf52833.ld
|
||||
HEAP_SIZE := 2048
|
||||
STACK_SIZE := 2048
|
||||
else ifeq ($(PLATFORM),E73_2G4M08S1C)
|
||||
SRC_FILES := \
|
||||
$(SDK_ROOT)/modules/nrfx/mdk/gcc_startup_nrf52840.S \
|
||||
$(SDK_ROOT)/modules/nrfx/mdk/system_nrf52840.c
|
||||
TARGETS := nrf52840_xxaa
|
||||
CFLAGS += -DNRF52840_XXAA
|
||||
ASMFLAGS += -DNRF52840_XXAA
|
||||
LINKER_SCRIPT := b_parasite_gcc_nrf52840.ld
|
||||
HEAP_SIZE := 8192
|
||||
STACK_SIZE := 8192
|
||||
endif
|
||||
|
||||
|
||||
PROJECT_NAME := ble_app_beacon_pca10056_s140
|
||||
OUTPUT_DIRECTORY := _build
|
||||
|
||||
# Set SDK_ROOT via env vars.
|
||||
# SDK_ROOT := ../../../.././..
|
||||
PROJ_DIR := ./src
|
||||
|
||||
# Source files common to all targets
|
||||
SRC_FILES += \
|
||||
$(SDK_ROOT)/components/libraries/log/src/nrf_log_backend_rtt.c \
|
||||
$(SDK_ROOT)/components/libraries/log/src/nrf_log_backend_serial.c \
|
||||
$(SDK_ROOT)/components/libraries/log/src/nrf_log_backend_uart.c \
|
||||
$(SDK_ROOT)/components/libraries/log/src/nrf_log_default_backends.c \
|
||||
$(SDK_ROOT)/components/libraries/log/src/nrf_log_frontend.c \
|
||||
$(SDK_ROOT)/components/libraries/log/src/nrf_log_str_formatter.c \
|
||||
$(SDK_ROOT)/components/libraries/button/app_button.c \
|
||||
$(SDK_ROOT)/components/libraries/util/app_error.c \
|
||||
$(SDK_ROOT)/components/libraries/util/app_error_handler_gcc.c \
|
||||
$(SDK_ROOT)/components/libraries/util/app_error_weak.c \
|
||||
$(SDK_ROOT)/components/libraries/scheduler/app_scheduler.c \
|
||||
$(SDK_ROOT)/components/libraries/timer/app_timer2.c \
|
||||
$(SDK_ROOT)/components/libraries/util/app_util_platform.c \
|
||||
$(SDK_ROOT)/components/libraries/timer/drv_rtc.c \
|
||||
$(SDK_ROOT)/components/libraries/hardfault/hardfault_implementation.c \
|
||||
$(SDK_ROOT)/components/libraries/util/nrf_assert.c \
|
||||
$(SDK_ROOT)/components/libraries/atomic_fifo/nrf_atfifo.c \
|
||||
$(SDK_ROOT)/components/libraries/atomic/nrf_atomic.c \
|
||||
$(SDK_ROOT)/components/libraries/balloc/nrf_balloc.c \
|
||||
$(SDK_ROOT)/external/fprintf/nrf_fprintf.c \
|
||||
$(SDK_ROOT)/external/fprintf/nrf_fprintf_format.c \
|
||||
$(SDK_ROOT)/components/libraries/memobj/nrf_memobj.c \
|
||||
$(SDK_ROOT)/components/libraries/pwr_mgmt/nrf_pwr_mgmt.c \
|
||||
$(SDK_ROOT)/components/libraries/ringbuf/nrf_ringbuf.c \
|
||||
$(SDK_ROOT)/components/libraries/experimental_section_vars/nrf_section_iter.c \
|
||||
$(SDK_ROOT)/components/libraries/sortlist/nrf_sortlist.c \
|
||||
$(SDK_ROOT)/components/libraries/strerror/nrf_strerror.c \
|
||||
$(SDK_ROOT)/integration/nrfx/legacy/nrf_drv_clock.c \
|
||||
$(SDK_ROOT)/integration/nrfx/legacy/nrf_drv_twi.c \
|
||||
$(SDK_ROOT)/modules/nrfx/soc/nrfx_atomic.c \
|
||||
$(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_clock.c \
|
||||
$(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_gpiote.c \
|
||||
$(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_rtc.c \
|
||||
$(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_saadc.c \
|
||||
$(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_twi.c \
|
||||
$(SDK_ROOT)/modules/nrfx/drivers/src/prs/nrfx_prs.c \
|
||||
$(SDK_ROOT)/modules/nrfx/drivers/src/nrfx_pwm.c \
|
||||
$(PROJ_DIR)/main.c \
|
||||
$(SDK_ROOT)/external/segger_rtt/SEGGER_RTT.c \
|
||||
$(SDK_ROOT)/external/segger_rtt/SEGGER_RTT_Syscalls_GCC.c \
|
||||
$(SDK_ROOT)/external/segger_rtt/SEGGER_RTT_printf.c \
|
||||
$(SDK_ROOT)/components/ble/common/ble_advdata.c \
|
||||
$(SDK_ROOT)/components/ble/common/ble_srv_common.c \
|
||||
$(SDK_ROOT)/external/utf_converter/utf.c \
|
||||
$(SDK_ROOT)/components/softdevice/common/nrf_sdh.c \
|
||||
$(SDK_ROOT)/components/softdevice/common/nrf_sdh_ble.c \
|
||||
$(SDK_ROOT)/components/softdevice/common/nrf_sdh_soc.c \
|
||||
$(PROJ_DIR)/prst/adc.c \
|
||||
$(PROJ_DIR)/prst/ble.c \
|
||||
$(PROJ_DIR)/prst/pwm.c \
|
||||
$(PROJ_DIR)/prst/rtc.c \
|
||||
$(PROJ_DIR)/prst/shtc3.c \
|
||||
|
||||
# Include folders common to all targets
|
||||
INC_FOLDERS += \
|
||||
$(SDK_ROOT)/components/nfc/ndef/generic/message \
|
||||
$(SDK_ROOT)/components/nfc/t2t_lib \
|
||||
$(SDK_ROOT)/components/nfc/t4t_parser/hl_detection_procedure \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_ancs_c \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_ias_c \
|
||||
$(SDK_ROOT)/components/libraries/pwm \
|
||||
$(SDK_ROOT)/components/libraries/usbd/class/cdc/acm \
|
||||
$(SDK_ROOT)/components/libraries/usbd/class/hid/generic \
|
||||
$(SDK_ROOT)/components/libraries/usbd/class/msc \
|
||||
$(SDK_ROOT)/components/libraries/usbd/class/hid \
|
||||
$(SDK_ROOT)/modules/nrfx/hal \
|
||||
$(SDK_ROOT)/components/nfc/ndef/conn_hand_parser/le_oob_rec_parser \
|
||||
$(SDK_ROOT)/components/libraries/log \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_gls \
|
||||
$(SDK_ROOT)/components/libraries/fstorage \
|
||||
$(SDK_ROOT)/components/nfc/ndef/text \
|
||||
$(SDK_ROOT)/components/libraries/mutex \
|
||||
$(SDK_ROOT)/components/libraries/gpiote \
|
||||
$(SDK_ROOT)/components/libraries/bootloader/ble_dfu \
|
||||
$(SDK_ROOT)/components/nfc/ndef/connection_handover/common \
|
||||
$(SDK_ROOT)/components/nfc/ndef/generic/record \
|
||||
$(SDK_ROOT)/components/ble/ble_advertising \
|
||||
$(SDK_ROOT)/external/utf_converter \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_bas_c \
|
||||
$(SDK_ROOT)/modules/nrfx/drivers/include \
|
||||
$(SDK_ROOT)/components/libraries/experimental_task_manager \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_hrs_c \
|
||||
$(SDK_ROOT)/components/softdevice/s140/headers/nrf52 \
|
||||
$(SDK_ROOT)/components/nfc/ndef/connection_handover/le_oob_rec \
|
||||
$(SDK_ROOT)/components/libraries/queue \
|
||||
$(SDK_ROOT)/components/libraries/pwr_mgmt \
|
||||
$(SDK_ROOT)/components/ble/ble_dtm \
|
||||
$(SDK_ROOT)/components/toolchain/cmsis/include \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_rscs_c \
|
||||
$(SDK_ROOT)/components/ble/common \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_lls \
|
||||
$(SDK_ROOT)/components/nfc/platform \
|
||||
$(SDK_ROOT)/components/nfc/ndef/connection_handover/ac_rec \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_bas \
|
||||
$(SDK_ROOT)/components/libraries/mpu \
|
||||
$(SDK_ROOT)/components/libraries/experimental_section_vars \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_ans_c \
|
||||
$(SDK_ROOT)/components/libraries/slip \
|
||||
$(SDK_ROOT)/components/libraries/delay \
|
||||
$(SDK_ROOT)/components/libraries/csense_drv \
|
||||
$(SDK_ROOT)/components/libraries/memobj \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_nus_c \
|
||||
$(SDK_ROOT)/components/softdevice/common \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_ias \
|
||||
$(SDK_ROOT)/components/libraries/usbd/class/hid/mouse \
|
||||
$(SDK_ROOT)/components/libraries/low_power_pwm \
|
||||
$(SDK_ROOT)/components/nfc/ndef/conn_hand_parser/ble_oob_advdata_parser \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_dfu \
|
||||
$(SDK_ROOT)/external/fprintf \
|
||||
$(SDK_ROOT)/components/libraries/svc \
|
||||
$(SDK_ROOT)/components/libraries/atomic \
|
||||
$(SDK_ROOT)/components \
|
||||
$(SDK_ROOT)/components/libraries/scheduler \
|
||||
$(SDK_ROOT)/components/libraries/cli \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_lbs \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_hts \
|
||||
$(SDK_ROOT)/components/libraries/crc16 \
|
||||
$(SDK_ROOT)/components/nfc/t4t_parser/apdu \
|
||||
$(SDK_ROOT)/components/libraries/util \
|
||||
./config \
|
||||
$(PROJ_DIR) \
|
||||
$(SDK_ROOT)/components/libraries/usbd/class/cdc \
|
||||
$(SDK_ROOT)/components/libraries/csense \
|
||||
$(SDK_ROOT)/components/libraries/balloc \
|
||||
$(SDK_ROOT)/components/libraries/ecc \
|
||||
$(SDK_ROOT)/components/libraries/hardfault \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_cscs \
|
||||
$(SDK_ROOT)/components/libraries/hci \
|
||||
$(SDK_ROOT)/components/libraries/timer \
|
||||
$(SDK_ROOT)/components/softdevice/s140/headers \
|
||||
$(SDK_ROOT)/integration/nrfx \
|
||||
$(SDK_ROOT)/components/nfc/t4t_parser/tlv \
|
||||
$(SDK_ROOT)/components/libraries/sortlist \
|
||||
$(SDK_ROOT)/components/libraries/spi_mngr \
|
||||
$(SDK_ROOT)/components/libraries/led_softblink \
|
||||
$(SDK_ROOT)/components/nfc/ndef/conn_hand_parser \
|
||||
$(SDK_ROOT)/components/libraries/sdcard \
|
||||
$(SDK_ROOT)/components/nfc/ndef/parser/record \
|
||||
$(SDK_ROOT)/modules/nrfx/mdk \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_cts_c \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_nus \
|
||||
$(SDK_ROOT)/components/libraries/twi_mngr \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_hids \
|
||||
$(SDK_ROOT)/components/libraries/strerror \
|
||||
$(SDK_ROOT)/components/libraries/crc32 \
|
||||
$(SDK_ROOT)/components/nfc/ndef/connection_handover/ble_oob_advdata \
|
||||
$(SDK_ROOT)/components/nfc/t2t_parser \
|
||||
$(SDK_ROOT)/components/nfc/ndef/connection_handover/ble_pair_msg \
|
||||
$(SDK_ROOT)/components/libraries/usbd/class/audio \
|
||||
$(SDK_ROOT)/components/nfc/t4t_lib \
|
||||
$(SDK_ROOT)/components/ble/peer_manager \
|
||||
$(SDK_ROOT)/components/libraries/mem_manager \
|
||||
$(SDK_ROOT)/components/libraries/ringbuf \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_tps \
|
||||
$(SDK_ROOT)/components/nfc/ndef/parser/message \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_dis \
|
||||
$(SDK_ROOT)/components/nfc/ndef/uri \
|
||||
$(SDK_ROOT)/components/nfc/t4t_parser/cc_file \
|
||||
$(SDK_ROOT)/components/ble/nrf_ble_qwr \
|
||||
$(SDK_ROOT)/components/libraries/gfx \
|
||||
$(SDK_ROOT)/components/libraries/button \
|
||||
$(SDK_ROOT)/modules/nrfx \
|
||||
$(SDK_ROOT)/components/libraries/twi_sensor \
|
||||
$(SDK_ROOT)/integration/nrfx/legacy \
|
||||
$(SDK_ROOT)/components/libraries/usbd/class/hid/kbd \
|
||||
$(SDK_ROOT)/components/nfc/ndef/connection_handover/ep_oob_rec \
|
||||
$(SDK_ROOT)/external/segger_rtt \
|
||||
$(SDK_ROOT)/components/libraries/atomic_fifo \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_lbs_c \
|
||||
$(SDK_ROOT)/components/nfc/ndef/connection_handover/ble_pair_lib \
|
||||
$(SDK_ROOT)/components/libraries/crypto \
|
||||
$(SDK_ROOT)/components/ble/ble_racp \
|
||||
$(SDK_ROOT)/components/libraries/fds \
|
||||
$(SDK_ROOT)/components/nfc/ndef/launchapp \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_hrs \
|
||||
$(SDK_ROOT)/components/ble/ble_services/ble_rscs \
|
||||
$(SDK_ROOT)/components/nfc/ndef/connection_handover/hs_rec \
|
||||
$(SDK_ROOT)/components/libraries/usbd \
|
||||
$(SDK_ROOT)/components/nfc/ndef/conn_hand_parser/ac_rec_parser \
|
||||
$(SDK_ROOT)/components/libraries/stack_guard \
|
||||
$(SDK_ROOT)/components/libraries/log/src \
|
||||
|
||||
# Libraries common to all targets
|
||||
LIB_FILES += \
|
||||
|
||||
# Optimization flags
|
||||
OPT = -O3 -g3
|
||||
# Uncomment the line below to enable link time optimization
|
||||
#OPT += -flto
|
||||
|
||||
# C flags common to all targets
|
||||
CFLAGS += $(OPT)
|
||||
CFLAGS += -DAPP_TIMER_V2
|
||||
CFLAGS += -DAPP_TIMER_V2_RTC1_ENABLED
|
||||
CFLAGS += -DCONFIG_GPIO_AS_PINRESET
|
||||
CFLAGS += -DFLOAT_ABI_HARD
|
||||
CFLAGS += -DNRF_SD_BLE_API_VERSION=7
|
||||
CFLAGS += -DS140
|
||||
CFLAGS += -DSOFTDEVICE_PRESENT
|
||||
CFLAGS += -DDEBUG
|
||||
# Setting -DDEBUG prints info via JLkinkRTTLogger, but hides the stack trace
|
||||
# when debugging with VSCode's Cortex Debug extension.
|
||||
# CFLAGS += -DDEBUG
|
||||
CFLAGS += -mcpu=cortex-m4
|
||||
CFLAGS += -mthumb -mabi=aapcs
|
||||
CFLAGS += -Wall -Werror -Wno-maybe-uninitialized
|
||||
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
|
||||
# keep every function in a separate section, this allows linker to discard unused ones
|
||||
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
|
||||
CFLAGS += -fno-builtin -fshort-enums
|
||||
|
||||
# C++ flags common to all targets
|
||||
CXXFLAGS += $(OPT)
|
||||
# Assembler flags common to all targets
|
||||
ASMFLAGS += -g3
|
||||
ASMFLAGS += -mcpu=cortex-m4
|
||||
ASMFLAGS += -mthumb -mabi=aapcs
|
||||
ASMFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
|
||||
ASMFLAGS += -DAPP_TIMER_V2
|
||||
ASMFLAGS += -DAPP_TIMER_V2_RTC1_ENABLED
|
||||
ASMFLAGS += -DCONFIG_GPIO_AS_PINRESET
|
||||
ASMFLAGS += -DFLOAT_ABI_HARD
|
||||
ASMFLAGS += -DNRF_SD_BLE_API_VERSION=7
|
||||
ASMFLAGS += -DS140
|
||||
ASMFLAGS += -DSOFTDEVICE_PRESENT
|
||||
|
||||
# Linker flags
|
||||
LDFLAGS += $(OPT)
|
||||
LDFLAGS += -mthumb -mabi=aapcs -L$(SDK_ROOT)/modules/nrfx/mdk -T$(LINKER_SCRIPT)
|
||||
LDFLAGS += -mcpu=cortex-m4
|
||||
LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
|
||||
# let linker dump unused sections
|
||||
LDFLAGS += -Wl,--gc-sections
|
||||
# use newlib in nano version
|
||||
LDFLAGS += --specs=nano.specs
|
||||
|
||||
$(TARGETS): CFLAGS += -D__HEAP_SIZE=$(HEAP_SIZE)
|
||||
$(TARGETS): CFLAGS += -D__STACK_SIZE=$(STACK_SIZE)
|
||||
$(TARGETS): ASMFLAGS += -D__HEAP_SIZE=$(HEAP_SIZE)
|
||||
$(TARGETS): ASMFLAGS += -D__STACK_SIZE=$(STACK_SIZE)
|
||||
|
||||
# Add standard libraries at the very end of the linker input, after all objects
|
||||
# that may need symbols provided by these libraries.
|
||||
LIB_FILES += -lc -lnosys -lm
|
||||
|
||||
|
||||
.PHONY: default help
|
||||
|
||||
# Default target - first one defined
|
||||
default: $(TARGETS)
|
||||
|
||||
# Print all targets that can be built
|
||||
help:
|
||||
@echo following targets are available:
|
||||
@echo $(TARGETS)
|
||||
@echo flash_softdevice
|
||||
@echo sdk_config - starting external tool for editing sdk_config.h
|
||||
@echo flash - flashing binary
|
||||
|
||||
TEMPLATE_PATH := $(SDK_ROOT)/components/toolchain/gcc
|
||||
|
||||
|
||||
include $(TEMPLATE_PATH)/Makefile.common
|
||||
|
||||
$(foreach target, $(TARGETS), $(call define_target, $(target)))
|
||||
|
||||
.PHONY: flash flash_softdevice erase
|
||||
|
||||
# Flash the program
|
||||
flash: default
|
||||
@echo Flashing: $(OUTPUT_DIRECTORY)/$(TARGETS).hex
|
||||
nrfjprog -f nrf52 --program $(OUTPUT_DIRECTORY)/$(TARGETS).hex --sectorerase
|
||||
nrfjprog -f nrf52 --reset
|
||||
|
||||
# Flash softdevice
|
||||
flash_softdevice:
|
||||
@echo Flashing: s140_nrf52_7.2.0_softdevice.hex
|
||||
nrfjprog -f nrf52 --program $(SDK_ROOT)/components/softdevice/s140/hex/s140_nrf52_7.2.0_softdevice.hex --sectorerase
|
||||
nrfjprog -f nrf52 --reset
|
||||
|
||||
erase:
|
||||
nrfjprog -f nrf52 --eraseall
|
||||
|
||||
SDK_CONFIG_FILE := ../config/sdk_config.h
|
||||
CMSIS_CONFIG_TOOL := $(SDK_ROOT)/external_tools/cmsisconfig/CMSIS_Configuration_Wizard.jar
|
||||
sdk_config:
|
||||
java -jar $(CMSIS_CONFIG_TOOL) $(SDK_CONFIG_FILE)
|
||||
|
||||
.PHONY: flash_loop
|
||||
flash_loop:
|
||||
while [ 1 ]; do make flash && break; done
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
find src -iname *.h -o -iname *.c | xargs clang-format -n -Werror
|
||||
|
||||
.PHONY: fix
|
||||
lint-fix:
|
||||
find src -iname *.h -o -iname *.c | xargs clang-format -i
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
# Overview
|
||||
|
||||
This is the b-parasite formware based on Nordic's [nRF5 SDK](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fstruct_sdk%2Fstruct%2Fsdk_nrf5_latest.html&cp=7_1).
|
||||
|
||||
It uses Nordic's SoftDevice, which should additionally be flashed to the chip before running our firmware.
|
||||
|
||||
I use a [JLink probe](https://www.segger.com/products/debug-probes/j-link/) for flashing and debugging.
|
||||
|
||||
# Configuration
|
||||
The b-parasite specific configuration, such as active/sleep time and transmitting power are defined in [config/prst_config.h](./config/prst_config.h).
|
||||
|
||||
# Flashing SoftDevice and Firmware
|
||||
```bash
|
||||
# Flash softdevice
|
||||
$ SDK_ROOT=~/dev/nrf52/sdk/nRF5_SDK_17.0.2_d674dde make flash_softdevice
|
||||
# Compile annd flash our firmware
|
||||
$ SDK_ROOT=~/dev/nrf52/sdk/nRF5_SDK_17.0.2_d674dde make flash
|
||||
```
|
||||
|
||||
# Debugging
|
||||
Calls to `NRF_LOG` will be readable on the console using `JLinkRTTLogger`. This is the handy one-liner I use for pulling log messages:
|
||||
|
||||
```bash
|
||||
$ echo "\n\n\n\n0\n/dev/stdout" | JLinkRTTLogger | sed 's/^.*app: //'
|
||||
```
|
||||
|
||||
# Bluetooth Low Energy Advertisement Data Encoding
|
||||
Sensor data is encoded in the BLE advertisement packet as Service Data for the [Environmental Sensing Service profile](https://www.bluetooth.com/specifications/assigned-numbers/environmental-sensing-service-characteristics/) (UUID 0x181a).
|
||||
|
||||
Sensor data is encoded in unsigned 16 bits (2 bytes), and whenever multiple
|
||||
bytes are used to represent a single value, the encoding is big-endian.
|
||||
|
||||
| Byte index | Description |
|
||||
|------------|-------------------------------------------------------------------|
|
||||
| 0 | Protocol version (4 bits) + reserved (3 bits) + `has_lux`* (1 bit)|
|
||||
| 1 | Reserved (4 bits) + increasing, wrap-around counter (4 bits) |
|
||||
| 2-3 | Battery voltage in millivolts |
|
||||
| 4-5 | Temp in 1000 * Celsius (protocol v1) or 100 * Celsius (v2) |
|
||||
| 6-7 | Relative air humidity, scaled from 0 (0%) to 0xffff (100%) |
|
||||
| 8-9 | Soil moisture, scaled from from 0 (0%) to 0xffff (100%) |
|
||||
| 10-15 | b-parasite's own MAC address |
|
||||
| 16-17* | Ambient light in lux |
|
||||
|
||||
\* If the `has_lux` bit is set, bytes 16-17 shall contain the ambient light in lux.
|
||||
If the `has_lux` bit is not set, bytes 16-17 may not exist or may contain
|
||||
meaningless data. The reasons for this behavior are:
|
||||
1. b-parasite version 1.0.x has no light sensor and its advertisement data may
|
||||
have only 16 bytes if its using an older firmware. In this case, `has_lux` shall
|
||||
never be set;
|
||||
2. b-parasite version 1.1.x has space for an optional LDR. Users can configure
|
||||
whether or not they have added the LDR by setting the `PRST_HAS_LDR` to 1 in
|
||||
prst_config.h.
|
||||
|
||||
# Supported Modules
|
||||
|
||||
This code supports two E73 modules:
|
||||
* E73-2G4M08S1C (nrf2480, default)
|
||||
* E73-2G4M08S1E (nrf2833)
|
||||
|
||||
To choose for which one you want to compile, just pass PLATFORM as an env variable to make, and set it to the platform you want to use. For example, to compile for E73-2G4M08S1E:
|
||||
|
||||
|
||||
```bash
|
||||
SDK_ROOT=<...> PLATFORM=E73_2G4M08S1E make
|
||||
```
|
||||
|
||||
and vice-versa for E73-2G4M08S1C, although that platform will be chosen as default anyways:
|
||||
|
||||
|
||||
```bash
|
||||
SDK_ROOT=<...> PLATFORM=E73_2G4M08S1C make
|
||||
```
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
/* Linker script to configure memory regions. */
|
||||
|
||||
SEARCH_DIR(.)
|
||||
GROUP(-lgcc -lc -lnosys)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x27000, LENGTH = 0x59000
|
||||
RAM (rwx) : ORIGIN = 0x20002300, LENGTH = 0x1dd00
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = ALIGN(4);
|
||||
.mem_section_dummy_ram :
|
||||
{
|
||||
}
|
||||
.cli_sorted_cmd_ptrs :
|
||||
{
|
||||
PROVIDE(__start_cli_sorted_cmd_ptrs = .);
|
||||
KEEP(*(.cli_sorted_cmd_ptrs))
|
||||
PROVIDE(__stop_cli_sorted_cmd_ptrs = .);
|
||||
} > RAM
|
||||
.fs_data :
|
||||
{
|
||||
PROVIDE(__start_fs_data = .);
|
||||
KEEP(*(.fs_data))
|
||||
PROVIDE(__stop_fs_data = .);
|
||||
} > RAM
|
||||
.log_dynamic_data :
|
||||
{
|
||||
PROVIDE(__start_log_dynamic_data = .);
|
||||
KEEP(*(SORT(.log_dynamic_data*)))
|
||||
PROVIDE(__stop_log_dynamic_data = .);
|
||||
} > RAM
|
||||
.log_filter_data :
|
||||
{
|
||||
PROVIDE(__start_log_filter_data = .);
|
||||
KEEP(*(SORT(.log_filter_data*)))
|
||||
PROVIDE(__stop_log_filter_data = .);
|
||||
} > RAM
|
||||
|
||||
} INSERT AFTER .data;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.mem_section_dummy_rom :
|
||||
{
|
||||
}
|
||||
.sdh_ble_observers :
|
||||
{
|
||||
PROVIDE(__start_sdh_ble_observers = .);
|
||||
KEEP(*(SORT(.sdh_ble_observers*)))
|
||||
PROVIDE(__stop_sdh_ble_observers = .);
|
||||
} > FLASH
|
||||
.sdh_soc_observers :
|
||||
{
|
||||
PROVIDE(__start_sdh_soc_observers = .);
|
||||
KEEP(*(SORT(.sdh_soc_observers*)))
|
||||
PROVIDE(__stop_sdh_soc_observers = .);
|
||||
} > FLASH
|
||||
.sdh_req_observers :
|
||||
{
|
||||
PROVIDE(__start_sdh_req_observers = .);
|
||||
KEEP(*(SORT(.sdh_req_observers*)))
|
||||
PROVIDE(__stop_sdh_req_observers = .);
|
||||
} > FLASH
|
||||
.sdh_state_observers :
|
||||
{
|
||||
PROVIDE(__start_sdh_state_observers = .);
|
||||
KEEP(*(SORT(.sdh_state_observers*)))
|
||||
PROVIDE(__stop_sdh_state_observers = .);
|
||||
} > FLASH
|
||||
.sdh_stack_observers :
|
||||
{
|
||||
PROVIDE(__start_sdh_stack_observers = .);
|
||||
KEEP(*(SORT(.sdh_stack_observers*)))
|
||||
PROVIDE(__stop_sdh_stack_observers = .);
|
||||
} > FLASH
|
||||
.nrf_queue :
|
||||
{
|
||||
PROVIDE(__start_nrf_queue = .);
|
||||
KEEP(*(.nrf_queue))
|
||||
PROVIDE(__stop_nrf_queue = .);
|
||||
} > FLASH
|
||||
.nrf_balloc :
|
||||
{
|
||||
PROVIDE(__start_nrf_balloc = .);
|
||||
KEEP(*(.nrf_balloc))
|
||||
PROVIDE(__stop_nrf_balloc = .);
|
||||
} > FLASH
|
||||
.cli_command :
|
||||
{
|
||||
PROVIDE(__start_cli_command = .);
|
||||
KEEP(*(.cli_command))
|
||||
PROVIDE(__stop_cli_command = .);
|
||||
} > FLASH
|
||||
.crypto_data :
|
||||
{
|
||||
PROVIDE(__start_crypto_data = .);
|
||||
KEEP(*(SORT(.crypto_data*)))
|
||||
PROVIDE(__stop_crypto_data = .);
|
||||
} > FLASH
|
||||
.pwr_mgmt_data :
|
||||
{
|
||||
PROVIDE(__start_pwr_mgmt_data = .);
|
||||
KEEP(*(SORT(.pwr_mgmt_data*)))
|
||||
PROVIDE(__stop_pwr_mgmt_data = .);
|
||||
} > FLASH
|
||||
.log_const_data :
|
||||
{
|
||||
PROVIDE(__start_log_const_data = .);
|
||||
KEEP(*(SORT(.log_const_data*)))
|
||||
PROVIDE(__stop_log_const_data = .);
|
||||
} > FLASH
|
||||
.log_backends :
|
||||
{
|
||||
PROVIDE(__start_log_backends = .);
|
||||
KEEP(*(SORT(.log_backends*)))
|
||||
PROVIDE(__stop_log_backends = .);
|
||||
} > FLASH
|
||||
|
||||
} INSERT AFTER .text
|
||||
|
||||
|
||||
INCLUDE "nrf_common.ld"
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
/* Linker script to configure memory regions. */
|
||||
|
||||
SEARCH_DIR(.)
|
||||
GROUP(-lgcc -lc -lnosys)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x27000, LENGTH = 0xd9000
|
||||
RAM (rwx) : ORIGIN = 0x20002300, LENGTH = 0x3dd00
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = ALIGN(4);
|
||||
.mem_section_dummy_ram :
|
||||
{
|
||||
}
|
||||
.cli_sorted_cmd_ptrs :
|
||||
{
|
||||
PROVIDE(__start_cli_sorted_cmd_ptrs = .);
|
||||
KEEP(*(.cli_sorted_cmd_ptrs))
|
||||
PROVIDE(__stop_cli_sorted_cmd_ptrs = .);
|
||||
} > RAM
|
||||
.fs_data :
|
||||
{
|
||||
PROVIDE(__start_fs_data = .);
|
||||
KEEP(*(.fs_data))
|
||||
PROVIDE(__stop_fs_data = .);
|
||||
} > RAM
|
||||
.log_dynamic_data :
|
||||
{
|
||||
PROVIDE(__start_log_dynamic_data = .);
|
||||
KEEP(*(SORT(.log_dynamic_data*)))
|
||||
PROVIDE(__stop_log_dynamic_data = .);
|
||||
} > RAM
|
||||
.log_filter_data :
|
||||
{
|
||||
PROVIDE(__start_log_filter_data = .);
|
||||
KEEP(*(SORT(.log_filter_data*)))
|
||||
PROVIDE(__stop_log_filter_data = .);
|
||||
} > RAM
|
||||
|
||||
} INSERT AFTER .data;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.mem_section_dummy_rom :
|
||||
{
|
||||
}
|
||||
.sdh_soc_observers :
|
||||
{
|
||||
PROVIDE(__start_sdh_soc_observers = .);
|
||||
KEEP(*(SORT(.sdh_soc_observers*)))
|
||||
PROVIDE(__stop_sdh_soc_observers = .);
|
||||
} > FLASH
|
||||
.sdh_ble_observers :
|
||||
{
|
||||
PROVIDE(__start_sdh_ble_observers = .);
|
||||
KEEP(*(SORT(.sdh_ble_observers*)))
|
||||
PROVIDE(__stop_sdh_ble_observers = .);
|
||||
} > FLASH
|
||||
.pwr_mgmt_data :
|
||||
{
|
||||
PROVIDE(__start_pwr_mgmt_data = .);
|
||||
KEEP(*(SORT(.pwr_mgmt_data*)))
|
||||
PROVIDE(__stop_pwr_mgmt_data = .);
|
||||
} > FLASH
|
||||
.sdh_req_observers :
|
||||
{
|
||||
PROVIDE(__start_sdh_req_observers = .);
|
||||
KEEP(*(SORT(.sdh_req_observers*)))
|
||||
PROVIDE(__stop_sdh_req_observers = .);
|
||||
} > FLASH
|
||||
.sdh_state_observers :
|
||||
{
|
||||
PROVIDE(__start_sdh_state_observers = .);
|
||||
KEEP(*(SORT(.sdh_state_observers*)))
|
||||
PROVIDE(__stop_sdh_state_observers = .);
|
||||
} > FLASH
|
||||
.sdh_stack_observers :
|
||||
{
|
||||
PROVIDE(__start_sdh_stack_observers = .);
|
||||
KEEP(*(SORT(.sdh_stack_observers*)))
|
||||
PROVIDE(__stop_sdh_stack_observers = .);
|
||||
} > FLASH
|
||||
.nrf_queue :
|
||||
{
|
||||
PROVIDE(__start_nrf_queue = .);
|
||||
KEEP(*(.nrf_queue))
|
||||
PROVIDE(__stop_nrf_queue = .);
|
||||
} > FLASH
|
||||
.nrf_balloc :
|
||||
{
|
||||
PROVIDE(__start_nrf_balloc = .);
|
||||
KEEP(*(.nrf_balloc))
|
||||
PROVIDE(__stop_nrf_balloc = .);
|
||||
} > FLASH
|
||||
.cli_command :
|
||||
{
|
||||
PROVIDE(__start_cli_command = .);
|
||||
KEEP(*(.cli_command))
|
||||
PROVIDE(__stop_cli_command = .);
|
||||
} > FLASH
|
||||
.crypto_data :
|
||||
{
|
||||
PROVIDE(__start_crypto_data = .);
|
||||
KEEP(*(SORT(.crypto_data*)))
|
||||
PROVIDE(__stop_crypto_data = .);
|
||||
} > FLASH
|
||||
.log_const_data :
|
||||
{
|
||||
PROVIDE(__start_log_const_data = .);
|
||||
KEEP(*(SORT(.log_const_data*)))
|
||||
PROVIDE(__stop_log_const_data = .);
|
||||
} > FLASH
|
||||
.log_backends :
|
||||
{
|
||||
PROVIDE(__start_log_backends = .);
|
||||
KEEP(*(SORT(.log_backends*)))
|
||||
PROVIDE(__stop_log_backends = .);
|
||||
} > FLASH
|
||||
|
||||
} INSERT AFTER .text
|
||||
|
||||
|
||||
INCLUDE "nrf_common.ld"
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
#ifndef _PRST_CONFIG_H_
|
||||
#define _PRST_CONFIG_H_
|
||||
|
||||
#include "nrf_gpio.h"
|
||||
|
||||
// Some configurations are version-specific. Uncomment the line corresponding
|
||||
// the the version you're programming. The version can be found on the
|
||||
// b-parasite board.
|
||||
// #define PRST_VERSION_1_0_X
|
||||
// #define PRST_VERSION_1_1_X
|
||||
#define PRST_VERSION_1_2_X
|
||||
|
||||
// Built-in LED.
|
||||
// Wether or not to turn the LED on/off during the wake-up cycle. Impacts
|
||||
// battery life.
|
||||
#define PRST_BLINK_LED 0
|
||||
#define PRST_LED_PIN NRF_GPIO_PIN_MAP(0, 28)
|
||||
|
||||
// Deep sleep.
|
||||
#define PRST_DEEP_SLEEP_IN_SECONDS 300
|
||||
|
||||
// Analog to digital converter (ADC).
|
||||
// Prints out ADC debug info, such as the values read for battery and soil
|
||||
// moisture.
|
||||
#define PRST_ADC_BATT_DEBUG 0
|
||||
#define PRST_ADC_SOIL_DEBUG 0
|
||||
|
||||
// BLE.
|
||||
// Prints out BLE debug info, such as the final encoded advertisement packet.
|
||||
#define PRST_BLE_DEBUG 0
|
||||
// The BLE protocol version defines how the sensors' data is encoded inside the
|
||||
// BLE advertisement packet. Possible values are 1 and 2.
|
||||
#define PRST_BLE_PROTOCOL_VERSION 1
|
||||
|
||||
// There are two options for configuring the MAC address of b-parasites:
|
||||
// 1. Comment out the PRST_BLE_MAC_ADDR to use a random static MAC address that
|
||||
// is preprogrammed in each nRF52 chip.
|
||||
// 2. Manually specify the MAC address you want below. In this scenario, the
|
||||
// following constraints must be met to ensure valid random static MAC
|
||||
// addresses:
|
||||
// a. Two most significant bits are set to 1;
|
||||
// b. The remaining bits should not _all_ be set to 0;
|
||||
// c. The remaining bits should not _all_ be set to 1;
|
||||
#define PRST_BLE_MAC_ADDR "f0:ca:f0:ca:01:01"
|
||||
|
||||
#define PRST_BLE_ADV_NAME "prst"
|
||||
// Total time spend advertising.
|
||||
#define PRST_BLE_ADV_TIME_IN_MS 1000
|
||||
// Interval between advertising packets.
|
||||
// From the specs, this value has to be greater or equal 20ms.
|
||||
#define PRST_BLE_ADV_INTERVAL_IN_MS 30
|
||||
// Possible values are ..., -8, -4, 0, 4, 8.
|
||||
#define PRST_BLE_ADV_TX_POWER 8
|
||||
|
||||
// PWM.
|
||||
#define PRST_PWM_PIN NRF_GPIO_PIN_MAP(0, 5)
|
||||
|
||||
#ifdef NRF52833_XXAA
|
||||
#define PRST_FAST_DISCH_PIN NRF_GPIO_PIN_MAP(0, 25)
|
||||
#else
|
||||
#define PRST_FAST_DISCH_PIN NRF_GPIO_PIN_MAP(1, 10)
|
||||
#endif
|
||||
|
||||
// SHT3C temp/humidity sensor.
|
||||
#define PRST_SHT3C_DEBUG 0
|
||||
|
||||
// Version-specific configuration.
|
||||
#if defined(PRST_VERSION_1_1_X)
|
||||
// The photoresistor (LDR) is optional in this revision. If set to 1, the LDR's
|
||||
// ADC channel will be sampled and its data will be encoded in the BLE
|
||||
// advertisement packet.
|
||||
#define PRST_HAS_LDR 1
|
||||
|
||||
// Light sensor pins.
|
||||
#define PRST_PHOTO_V_PIN NRF_GPIO_PIN_MAP(0, 29)
|
||||
#define PRST_PHOTO_OUT_PIN NRF_GPIO_PIN_MAP(0, 2)
|
||||
|
||||
// Whether to produce debug messages for the LDR
|
||||
#define PRST_ADC_PHOTO_DEBUG 0
|
||||
|
||||
#elif defined(PRST_VERSION_1_2_X)
|
||||
|
||||
#define PRST_HAS_PHOTOTRANSISTOR 1
|
||||
|
||||
#define PRST_PHOTO_V_PIN NRF_GPIO_PIN_MAP(0, 29)
|
||||
#define PRST_PHOTO_OUT_PIN NRF_GPIO_PIN_MAP(0, 2)
|
||||
|
||||
#define PRST_ADC_PHOTO_DEBUG 0
|
||||
|
||||
#endif // End of version-specific configuration.
|
||||
|
||||
#endif // _PRST_CONFIG_H_
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,120 +0,0 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "nrf_delay.h"
|
||||
#include "nrf_gpio.h"
|
||||
#include "nrf_log.h"
|
||||
#include "nrf_log_ctrl.h"
|
||||
#include "nrf_log_default_backends.h"
|
||||
#include "nrf_pwr_mgmt.h"
|
||||
#include "prst/adc.h"
|
||||
#include "prst/ble.h"
|
||||
#include "prst/pwm.h"
|
||||
#include "prst/rtc.h"
|
||||
#include "prst/shtc3.h"
|
||||
#include "prst_config.h"
|
||||
|
||||
// A small wrap-around counter for deduplicating BLE packets on the receiver.
|
||||
static uint8_t run_counter = 0;
|
||||
|
||||
static void log_init(void) {
|
||||
APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
|
||||
NRF_LOG_DEFAULT_BACKENDS_INIT();
|
||||
NRF_LOG_INFO("Log inited.");
|
||||
}
|
||||
|
||||
static void gpio_init(void) {
|
||||
nrf_gpio_cfg_output(PRST_LED_PIN);
|
||||
nrf_gpio_cfg_output(PRST_FAST_DISCH_PIN);
|
||||
#if PRST_HAS_LDR || PRST_HAS_PHOTOTRANSISTOR
|
||||
nrf_gpio_cfg_output(PRST_PHOTO_V_PIN);
|
||||
#endif
|
||||
NRF_LOG_INFO("GPIO pins inited.");
|
||||
}
|
||||
|
||||
static void power_management_init(void) {
|
||||
APP_ERROR_CHECK(nrf_pwr_mgmt_init());
|
||||
NRF_LOG_INFO("GPIO pins inited.");
|
||||
}
|
||||
|
||||
// This FPU exception mask trick is recommended for avoiding unwanted
|
||||
// interupts from the floating point unit. This would be pretty bad,
|
||||
// since it would wake us up from deep sleep for nothing.
|
||||
#define FPU_EXCEPTION_MASK 0x0000009F
|
||||
static void power_manage(void) {
|
||||
__set_FPSCR(__get_FPSCR() & ~(FPU_EXCEPTION_MASK));
|
||||
(void)__get_FPSCR();
|
||||
NVIC_ClearPendingIRQ(FPU_IRQn);
|
||||
nrf_pwr_mgmt_run();
|
||||
}
|
||||
|
||||
// This is the RTC callback in which we do all of our work as quickly as
|
||||
// possible:
|
||||
// - Measure the soil moisture;
|
||||
// - Measure the air temperature and humidity;
|
||||
// - Encode the measurements into the BLE advertisement packet;
|
||||
// - Turn on BLE advertising for a while;
|
||||
// - Turn everything off and return back to sleep.
|
||||
static void rtc_callback() {
|
||||
#if PRST_BLINK_LED
|
||||
nrf_gpio_pin_set(PRST_LED_PIN);
|
||||
#endif
|
||||
prst_shtc3_read_t temp_humi = prst_shtc3_read();
|
||||
nrf_gpio_pin_set(PRST_FAST_DISCH_PIN);
|
||||
prst_pwm_init();
|
||||
prst_pwm_start();
|
||||
prst_adc_batt_read_t batt_read = prst_adc_batt_read();
|
||||
prst_adc_soil_moisture_t soil_read = prst_adc_soil_read(batt_read.voltage);
|
||||
prst_pwm_stop();
|
||||
nrf_gpio_pin_clear(PRST_FAST_DISCH_PIN);
|
||||
|
||||
uint16_t lux = 0;
|
||||
#if PRST_HAS_LDR || PRST_HAS_PHOTOTRANSISTOR
|
||||
nrf_gpio_pin_set(PRST_PHOTO_V_PIN);
|
||||
nrf_delay_ms(50);
|
||||
prst_adc_photo_sensor_t photo_read = prst_adc_photo_read(batt_read.voltage);
|
||||
lux = photo_read.brightness;
|
||||
nrf_gpio_pin_clear(PRST_PHOTO_V_PIN);
|
||||
#endif
|
||||
|
||||
prst_ble_update_adv_data(batt_read.millivolts, temp_humi.temp_celsius,
|
||||
temp_humi.humidity, soil_read.relative, lux,
|
||||
run_counter);
|
||||
prst_adv_start();
|
||||
nrf_delay_ms(PRST_BLE_ADV_TIME_IN_MS);
|
||||
prst_adv_stop();
|
||||
#if PRST_BLINK_LED
|
||||
nrf_gpio_pin_clear(PRST_LED_PIN);
|
||||
#endif
|
||||
NRF_LOG_FLUSH();
|
||||
run_counter++;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
log_init();
|
||||
gpio_init();
|
||||
power_management_init();
|
||||
prst_ble_init();
|
||||
prst_adc_init();
|
||||
prst_shtc3_init();
|
||||
|
||||
// Quick LED flash.
|
||||
nrf_gpio_pin_set(PRST_LED_PIN);
|
||||
nrf_delay_ms(200);
|
||||
nrf_gpio_pin_clear(PRST_LED_PIN);
|
||||
|
||||
// Set up RTC. It will call our custom callback at a regular interval, defined
|
||||
// by PRST_DEEP_SLEEP_IN_SECONDS.
|
||||
prst_rtc_set_callback(rtc_callback);
|
||||
prst_rtc_init();
|
||||
|
||||
// In addition to scheduling it, let's immediatelly call it - it makes
|
||||
// debugging less tedious.
|
||||
rtc_callback();
|
||||
|
||||
// Here we go into a low energy mode. The datasheet calls this mode "System
|
||||
// ON", and in my tests it consumes around 2.7uA.
|
||||
for (;;) {
|
||||
power_manage();
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
BasedOnStyle: Google
|
||||
|
|
@ -1,182 +0,0 @@
|
|||
#include "prst/adc.h"
|
||||
|
||||
#include <app_error.h>
|
||||
#include <math.h>
|
||||
#include <nrf_drv_saadc.h>
|
||||
#include <nrf_log.h>
|
||||
#include <nrf_saadc.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "prst_config.h"
|
||||
|
||||
#define PRST_ADC_RESOLUTION 10
|
||||
|
||||
#define PRST_ADC_BATT_INPUT NRF_SAADC_INPUT_VDD
|
||||
#define PRST_ADC_BATT_CHANNEL 0
|
||||
|
||||
#define PRST_ADC_SOIL_INPUT NRF_SAADC_INPUT_AIN1
|
||||
#define PRST_ADC_SOIL_CHANNEL 1
|
||||
|
||||
#define PRST_ADC_PHOTO_INPUT NRF_SAADC_INPUT_AIN0
|
||||
#define PRST_ADC_PHOTO_CHANNEL 2
|
||||
|
||||
static nrf_saadc_value_t sample_adc_channel(uint8_t channel) {
|
||||
nrf_saadc_value_t result;
|
||||
// *WARNING* this function is blocking, which is ot ideal but okay, but it
|
||||
// *does not work* when oversampling is set! I had to manually disable
|
||||
// SAADC_CONFIG_OVERSAMPLE in sdk_config.h.
|
||||
APP_ERROR_CHECK(nrf_drv_saadc_sample_convert(channel, &result));
|
||||
return result;
|
||||
}
|
||||
|
||||
// Caps the argument to the [0.0, 1.0] range.
|
||||
static inline double cap_percentage(double value) {
|
||||
return value > 1.0 ? 1.0 : (value < 0.0 ? 0.0 : value);
|
||||
}
|
||||
|
||||
// Unused, since we'll call the SAADC synchronously for now.
|
||||
void saadc_callback(nrf_drv_saadc_evt_t const* p_event) {
|
||||
if (p_event->type == NRF_DRV_SAADC_EVT_DONE) {
|
||||
ret_code_t err_code;
|
||||
uint16_t size = p_event->data.done.size;
|
||||
|
||||
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, size);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
int i;
|
||||
NRF_LOG_INFO("[adc] ADC event!");
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
NRF_LOG_INFO("[adc] %d", p_event->data.done.p_buffer[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void prst_adc_init() {
|
||||
nrf_saadc_channel_config_t batt_channel_config =
|
||||
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(PRST_ADC_BATT_INPUT);
|
||||
|
||||
APP_ERROR_CHECK(nrf_drv_saadc_init(NULL, saadc_callback));
|
||||
|
||||
APP_ERROR_CHECK(
|
||||
nrf_drv_saadc_channel_init(PRST_ADC_BATT_CHANNEL, &batt_channel_config));
|
||||
|
||||
nrf_saadc_channel_config_t soil_channel_config =
|
||||
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(PRST_ADC_SOIL_INPUT);
|
||||
soil_channel_config.reference = NRF_SAADC_REFERENCE_VDD4;
|
||||
APP_ERROR_CHECK(
|
||||
nrf_drv_saadc_channel_init(PRST_ADC_SOIL_CHANNEL, &soil_channel_config));
|
||||
|
||||
nrf_saadc_channel_config_t photo_channel_config =
|
||||
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(PRST_ADC_PHOTO_INPUT);
|
||||
APP_ERROR_CHECK(nrf_drv_saadc_channel_init(PRST_ADC_PHOTO_CHANNEL,
|
||||
&photo_channel_config));
|
||||
}
|
||||
|
||||
prst_adc_batt_read_t prst_adc_batt_read() {
|
||||
nrf_saadc_value_t result = sample_adc_channel(PRST_ADC_BATT_CHANNEL);
|
||||
prst_adc_batt_read_t ret;
|
||||
ret.raw = (uint16_t)result;
|
||||
ret.voltage = (3.6 * result) / (1 << PRST_ADC_RESOLUTION);
|
||||
ret.millivolts = ret.voltage * 1000;
|
||||
#if PRST_ADC_BATT_DEBUG
|
||||
NRF_LOG_INFO(
|
||||
"[adc] Read battery voltage: %d (raw); %d mV; " NRF_LOG_FLOAT_MARKER " V",
|
||||
ret.raw, ret.millivolts, NRF_LOG_FLOAT(ret.voltage));
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
// If you got this far and really want to see how the sausage is made,
|
||||
// this function estimates the soil moisture percent based on the raw
|
||||
// ADC value as returned from the saadc. It assumes 10 bits resolution.
|
||||
// Ideally, we're taking the ADC sample relative to the VDD voltage, so
|
||||
// this input value should be stable across the range of input voltages.
|
||||
// In practice, when varying the input voltage, this value is drifting
|
||||
// enough to be annoying. To account for this drift, I collected ADC readings
|
||||
// while varying the input voltage from 2V to 3V (CR2032 voltage range) and
|
||||
// fitted two second degree polynomials over them - one for the sensor
|
||||
// out in the air (representing a dry soil) and one while holding the
|
||||
// sensor in my hand (representing a wet soil).
|
||||
// This raw data is available at the data/ dir at the root of this repository.
|
||||
static inline double get_soil_moisture_percent(
|
||||
double battery_voltage, nrf_saadc_value_t raw_adc_output) {
|
||||
const double x = battery_voltage;
|
||||
const double dry = -12.9 * x * x + 111 * x + 228;
|
||||
const double wet = -5.71 * x * x + 60.2 * x + 126;
|
||||
#if PRST_ADC_SOIL_DEBUG
|
||||
NRF_LOG_INFO("[adc] batt: " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(x));
|
||||
NRF_LOG_INFO("[adc] dry: " NRF_LOG_FLOAT_MARKER " wet: " NRF_LOG_FLOAT_MARKER,
|
||||
NRF_LOG_FLOAT(dry), NRF_LOG_FLOAT(wet));
|
||||
#endif
|
||||
return (raw_adc_output - dry) / (wet - dry);
|
||||
}
|
||||
|
||||
prst_adc_soil_moisture_t prst_adc_soil_read(double battery_voltage) {
|
||||
nrf_saadc_value_t raw_adc_output = sample_adc_channel(PRST_ADC_SOIL_CHANNEL);
|
||||
const double percentage =
|
||||
get_soil_moisture_percent(battery_voltage, raw_adc_output);
|
||||
prst_adc_soil_moisture_t ret;
|
||||
ret.raw = raw_adc_output;
|
||||
ret.percentage = percentage;
|
||||
ret.relative = cap_percentage(percentage) * UINT16_MAX;
|
||||
#if PRST_ADC_SOIL_DEBUG
|
||||
NRF_LOG_INFO("[adc] Read soil moisture: %d (raw); " NRF_LOG_FLOAT_MARKER
|
||||
" %% (percentage); %u (relative)",
|
||||
ret.raw, NRF_LOG_FLOAT(percentage * 100), ret.relative);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
prst_adc_photo_sensor_t prst_adc_photo_read(double battery_voltage) {
|
||||
nrf_saadc_value_t raw_photo_output =
|
||||
MAX(0, sample_adc_channel(PRST_ADC_PHOTO_CHANNEL));
|
||||
prst_adc_photo_sensor_t ret;
|
||||
ret.raw = raw_photo_output;
|
||||
ret.voltage = (3.6 * raw_photo_output) / (1 << PRST_ADC_RESOLUTION);
|
||||
|
||||
#if PRST_HAS_LDR
|
||||
// The photo resistor forms a voltage divider with a 10 kOhm resistor.
|
||||
// The voltage here is measured in the middle of the voltage divider.
|
||||
// Vcc ---- (R_photo) ---|--- (10k) ---- GND
|
||||
// Vout
|
||||
// So we can estimate R_photo = R * (Vcc - Vout) / Vout
|
||||
const float photo_resistance =
|
||||
1e4f * (battery_voltage - ret.voltage) / ret.voltage;
|
||||
|
||||
// The relationship between the LDR resistance and the lux level is
|
||||
// logarithmic. We need to solve a logarithmic equation to find the lux
|
||||
// level, given the LDR resistance we just measured.
|
||||
// These values work for the GL5528 LDR and were borrowed from
|
||||
// https://github.com/QuentinCG/Arduino-Light-Dependent-Resistor-Library.
|
||||
const float mult_value = 32017200.0f;
|
||||
const float pow_value = 1.5832f;
|
||||
ret.brightness =
|
||||
MAX(0, MIN(mult_value / powf(photo_resistance, pow_value), UINT16_MAX));
|
||||
|
||||
#elif PRST_HAS_PHOTOTRANSISTOR
|
||||
// The ALS-PT19 phototransistor is a device in which the current flow between
|
||||
// its two terminals is controlled by how much light there is in the ambient.
|
||||
// We measure that current by calculating the voltage across a resistor that
|
||||
// is connected in series with the phototransistor.
|
||||
const float phototransistor_resistor = 470.0f;
|
||||
const float current_sun = 3.59e-3f;
|
||||
// Assuming 10000 lux for the saturation test. Calibration with a proper light
|
||||
// meter would be better.
|
||||
const float lux_sun = 10000.0f;
|
||||
const float current = ret.voltage / phototransistor_resistor;
|
||||
ret.brightness = MAX(0, MIN(lux_sun * current / current_sun, UINT16_MAX));
|
||||
|
||||
#if PRST_ADC_PHOTO_DEBUG
|
||||
NRF_LOG_INFO("[adc] Phototransistor current: " NRF_LOG_FLOAT_MARKER " uA",
|
||||
NRF_LOG_FLOAT(1000000 * current));
|
||||
#endif // PRST_ADC_PHOTO_DEBUG
|
||||
#endif // PRST_HAS_PHOTOTRANSISTOR
|
||||
|
||||
#if PRST_ADC_PHOTO_DEBUG
|
||||
NRF_LOG_INFO("[adc] Read brightness level: " NRF_LOG_FLOAT_MARKER
|
||||
" mV %d (raw); %d (lux)",
|
||||
NRF_LOG_FLOAT(1000 * ret.voltage), ret.raw, ret.brightness);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#ifndef _PRST_ADC_H_
|
||||
#define _PRST_ADC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct prst_adc_batt_val {
|
||||
int16_t raw;
|
||||
uint16_t millivolts;
|
||||
double voltage;
|
||||
} prst_adc_batt_read_t;
|
||||
|
||||
typedef struct prst_adc_soil_moisture {
|
||||
int16_t raw;
|
||||
// A value from 0 (completely dry) to 2^10 (completely wet).
|
||||
uint16_t relative;
|
||||
double percentage;
|
||||
} prst_adc_soil_moisture_t;
|
||||
|
||||
typedef struct prst_adc_photo_sensor {
|
||||
int16_t raw;
|
||||
double voltage;
|
||||
// Value in lux.
|
||||
uint16_t brightness;
|
||||
} prst_adc_photo_sensor_t;
|
||||
|
||||
void prst_adc_init();
|
||||
|
||||
prst_adc_batt_read_t prst_adc_batt_read();
|
||||
|
||||
prst_adc_soil_moisture_t prst_adc_soil_read(double battery_voltage);
|
||||
|
||||
prst_adc_photo_sensor_t prst_adc_photo_read(double battery_voltage);
|
||||
|
||||
#endif // _PRST_ADC_H_
|
||||
|
|
@ -1,224 +0,0 @@
|
|||
#include "prst/ble.h"
|
||||
|
||||
#include <ble_advdata.h>
|
||||
#include <ble_gap.h>
|
||||
#include <nordic_common.h>
|
||||
#include <nrf_log.h>
|
||||
#include <nrf_sdh.h>
|
||||
#include <nrf_sdh_ble.h>
|
||||
|
||||
#include "prst_config.h"
|
||||
|
||||
// We need to pick a service UUID for broadcasting our sensor data.
|
||||
// 0x181a is defined as "environmental sensing", which seems appopriate.
|
||||
#define SERVICE_UUID 0x181a
|
||||
|
||||
// The connection to configure. We only have the one.
|
||||
#define PRST_CONN_CFG_TAG 1
|
||||
|
||||
#define NON_CONNECTABLE_ADV_INTERVAL \
|
||||
MSEC_TO_UNITS(PRST_BLE_ADV_INTERVAL_IN_MS, UNIT_0_625_MS)
|
||||
|
||||
// Sensor data payload that will go into the advertisement message.
|
||||
// We have a maximum of 20 bytes to play with here.
|
||||
// Sensor data is encoded in unsigned 16 bits (2 bytes), and whenever multiple
|
||||
// bytes are used to represent a single value, the encoding is big-endian:
|
||||
/*
|
||||
| Byte index | Description |
|
||||
|------------|-----------------------------------------------------------------|
|
||||
| 0 | Protocol version (4 bits) + reserved (3 bits) + has_lux* (1 bit)|
|
||||
| 1 | Reserved (4 bits) + increasing, wrap-around counter (4 bits) |
|
||||
| 2-3 | Battery voltage in millivolts |
|
||||
| 4-5 | Temp in 1000 * Celsius (protocol v1) or 100 * Celsius (v2) |
|
||||
| 6-7 | Relative air humidity, scaled from 0 (0%) to 0xffff (100%) |
|
||||
| 8-9 | Soil moisture, scaled from from 0 (0%) to 0xffff (100%) |
|
||||
| 10-15 | b-parasite's own MAC address |
|
||||
| 16-17* | Ambient light in lux |
|
||||
|
||||
* If the has_lux bit is set, bytes 16-17 shall contain the ambient light in lux.
|
||||
If the has_lux bit is not set, bytes 16-17 may not exist or may contain
|
||||
meaningless data. The reasons for this behavior are:
|
||||
1. b-parasite version 1.0.0 has no light sensor and its advertisement data may
|
||||
have only 16 bytes if its using an older firmware. In this case, has_lux shall
|
||||
never be set;
|
||||
2. b-parasite version 1.1.0 has space for an optional LDR. Users can configure
|
||||
whether or not they have added the LDR by setting the PRST_HAS_LDR to 1 in
|
||||
prst_config.h.
|
||||
*/
|
||||
|
||||
#define SERVICE_DATA_LEN 18
|
||||
static uint8_t service_data[SERVICE_DATA_LEN];
|
||||
|
||||
// Stores the encoded advertisement data. As per BLE spec, 31 bytes max.
|
||||
static uint8_t encoded_adv_data_[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
|
||||
|
||||
// Structure holding high level advertisement data and contains a pointer to
|
||||
// the actual encoded advertised bytes.
|
||||
static ble_gap_adv_data_t gap_adv_data_ = {
|
||||
.adv_data = {.p_data = encoded_adv_data_,
|
||||
.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX},
|
||||
.scan_rsp_data = {.p_data = NULL, .len = 0}};
|
||||
|
||||
// We'll put our sensor data inside an advertisement service.
|
||||
static ble_advdata_service_data_t advdata_service_data_ = {
|
||||
.service_uuid = SERVICE_UUID,
|
||||
.data = {
|
||||
.p_data = service_data,
|
||||
.size = SERVICE_DATA_LEN,
|
||||
}};
|
||||
|
||||
// Holds the service data to be broadcasted. The contents of this struct
|
||||
// will be encoded into gap_adv_data.
|
||||
// Warning: do not update this while advertising.
|
||||
static ble_advdata_t adv_data_ = {
|
||||
.name_type = BLE_ADVDATA_FULL_NAME,
|
||||
.flags = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED,
|
||||
.p_service_data_array = &advdata_service_data_,
|
||||
.service_data_count = 1,
|
||||
};
|
||||
|
||||
// NRF supports multiple advertisement sets. This initialization is a request
|
||||
// for configuring a new one.
|
||||
static uint8_t adv_handle_ = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
|
||||
|
||||
// Advertisement parameters.
|
||||
static ble_gap_adv_params_t adv_params_;
|
||||
|
||||
// Stores the MAC address & type.
|
||||
static ble_gap_addr_t gap_addr_ = {.addr_type =
|
||||
BLE_GAP_ADDR_TYPE_RANDOM_STATIC};
|
||||
|
||||
static void init_advertisement_data() {
|
||||
// We'll just broadcast our data, so we disallow connections and scan
|
||||
// requests.
|
||||
adv_params_.properties.type =
|
||||
BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED;
|
||||
|
||||
// No particular peer - undirected advertisement.
|
||||
adv_params_.p_peer_addr = NULL;
|
||||
adv_params_.filter_policy = BLE_GAP_ADV_FP_ANY;
|
||||
adv_params_.interval = NON_CONNECTABLE_ADV_INTERVAL;
|
||||
adv_params_.duration = 0; // Never time out.
|
||||
|
||||
ble_gap_conn_sec_mode_t sec_mode;
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
|
||||
sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *)PRST_BLE_ADV_NAME,
|
||||
strlen(PRST_BLE_ADV_NAME));
|
||||
|
||||
uint32_t err_code =
|
||||
sd_ble_gap_adv_set_configure(&adv_handle_, &gap_adv_data_, &adv_params_);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
// Four bits for the protocol version.
|
||||
service_data[0] |= (PRST_BLE_PROTOCOL_VERSION << 4) & 0xf0;
|
||||
|
||||
// Bit 0 of byte 0 specifies whether or not ambient light data exists in the
|
||||
// payload.
|
||||
#if PRST_HAS_LDR || PRST_HAS_PHOTOTRANSISTOR
|
||||
service_data[0] |= 1;
|
||||
#endif
|
||||
|
||||
// Bytes 10-15 (inclusive) contain the whole MAC address in big-endian.
|
||||
for (int i = 0; i < 6; i++) {
|
||||
service_data[10 + i] = gap_addr_.addr[5 - i];
|
||||
}
|
||||
}
|
||||
|
||||
void prst_ble_init() {
|
||||
uint32_t err_code;
|
||||
|
||||
// Enable SoftDevice request.
|
||||
err_code = nrf_sdh_enable_request();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
// Set the default config and get the starting RAM address.
|
||||
uint32_t ram_start = 0;
|
||||
err_code = nrf_sdh_ble_default_cfg_set(PRST_CONN_CFG_TAG, &ram_start);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
// Enable SoftDevice.
|
||||
err_code = nrf_sdh_ble_enable(&ram_start);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
#ifdef PRST_BLE_MAC_ADDR
|
||||
// Parses configured MAC address from PRST_BLE_MAC_ADDR.
|
||||
int mac_bytes[6];
|
||||
sscanf(PRST_BLE_MAC_ADDR, "%x:%x:%x:%x:%x:%x", &mac_bytes[0], &mac_bytes[1],
|
||||
&mac_bytes[2], &mac_bytes[3], &mac_bytes[4], &mac_bytes[5]);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
gap_addr_.addr[5 - i] = (uint8_t)mac_bytes[i];
|
||||
}
|
||||
APP_ERROR_CHECK(sd_ble_gap_addr_set(&gap_addr_));
|
||||
#endif
|
||||
|
||||
APP_ERROR_CHECK(sd_ble_gap_addr_get(&gap_addr_));
|
||||
NRF_LOG_INFO("[ble] MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||
gap_addr_.addr[5], gap_addr_.addr[4], gap_addr_.addr[3],
|
||||
gap_addr_.addr[2], gap_addr_.addr[1], gap_addr_.addr[0]);
|
||||
|
||||
init_advertisement_data();
|
||||
}
|
||||
|
||||
void prst_ble_update_adv_data(uint16_t batt_millivolts, float temp_celsius,
|
||||
uint16_t humidity, uint16_t soil_moisture,
|
||||
uint16_t brightness, uint8_t run_counter) {
|
||||
// 4 bits for a small wrap-around counter for deduplicating messages on the
|
||||
// receiver.
|
||||
service_data[1] = run_counter & 0x0f;
|
||||
|
||||
service_data[2] = batt_millivolts >> 8;
|
||||
service_data[3] = batt_millivolts & 0xff;
|
||||
|
||||
#if PRST_BLE_PROTOCOL_VERSION == 1
|
||||
uint16_t temp_millicelsius = temp_celsius * 1000;
|
||||
service_data[4] = temp_millicelsius >> 8;
|
||||
service_data[5] = temp_millicelsius & 0xff;
|
||||
#elif PRST_BLE_PROTOCOL_VERSION == 2
|
||||
int16_t temp_centicelsius = temp_celsius * 100;
|
||||
service_data[4] = temp_centicelsius >> 8;
|
||||
service_data[5] = temp_centicelsius & 0xff;
|
||||
#else
|
||||
#error "[ble] Unsupported BLE protocol version"
|
||||
#endif // PRST_BLE_PROTOCOL_VERSION
|
||||
|
||||
service_data[6] = humidity >> 8;
|
||||
service_data[7] = humidity & 0xff;
|
||||
|
||||
service_data[8] = soil_moisture >> 8;
|
||||
service_data[9] = soil_moisture & 0xff;
|
||||
|
||||
#if PRST_HAS_LDR || PRST_HAS_PHOTOTRANSISTOR
|
||||
service_data[16] = brightness >> 8;
|
||||
service_data[17] = brightness & 0xff;
|
||||
#endif
|
||||
|
||||
// Encodes adv_data_ into .gap_adv_data_.
|
||||
uint32_t err_code = ble_advdata_encode(
|
||||
&adv_data_, gap_adv_data_.adv_data.p_data, &gap_adv_data_.adv_data.len);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
#if PRST_BLE_DEBUG
|
||||
NRF_LOG_INFO("[ble] Encoded BLE adv packet:");
|
||||
for (int i = 0; i < sizeof(encoded_adv_data_); i++) {
|
||||
NRF_LOG_INFO("[ble] 0x%02x", encoded_adv_data_[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void prst_adv_start() {
|
||||
APP_ERROR_CHECK(sd_ble_gap_adv_start(adv_handle_, PRST_CONN_CFG_TAG));
|
||||
APP_ERROR_CHECK(sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV,
|
||||
adv_handle_, PRST_BLE_ADV_TX_POWER));
|
||||
#if PRST_BLE_DEBUG
|
||||
NRF_LOG_INFO("[ble] Advertising started.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void prst_adv_stop() {
|
||||
ret_code_t err_code;
|
||||
err_code = sd_ble_gap_adv_stop(adv_handle_);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
#if PRST_BLE_DEBUG
|
||||
NRF_LOG_INFO("[ble] Advertising stopped.\n");
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
#ifndef _PRST_BLE_H_
|
||||
#define _PRST_BLE_H_
|
||||
|
||||
#include <app_error.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Initializes SoftDevice.
|
||||
void prst_ble_init();
|
||||
|
||||
void prst_adv_start();
|
||||
|
||||
void prst_adv_stop();
|
||||
|
||||
void prst_ble_update_adv_data(uint16_t batt_millivolts, float temp_celsius,
|
||||
uint16_t humidity, uint16_t soil_moisture,
|
||||
uint16_t brightness, uint8_t run_counter);
|
||||
|
||||
#endif // _PRST_BLE_H_
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
#include "prst/pwm.h"
|
||||
|
||||
#include <app_error.h>
|
||||
#include <nordic_common.h>
|
||||
#include <nrf_drv_pwm.h>
|
||||
#include <nrf_log.h>
|
||||
#include <nrf_pwm.h>
|
||||
|
||||
#include "prst_config.h"
|
||||
|
||||
// Each step in the counter will take 1/16e6 s.
|
||||
#define PRST_PWM_BASE_FREQ NRF_PWM_CLK_16MHz
|
||||
// We will count up to 16. It will take 1us at 16MHz.
|
||||
// With the NRF_PWM_MODE_UP_AND_DOWN count mode, we assume 1us is half the
|
||||
// output PWM period (total 2us => 500MHz frequency). We set a duty cycle of
|
||||
// 50% below with PRST_PWM_FLIP_AT_COUNT to be half the max count.
|
||||
#define PRST_PWM_MAX_COUNT 16
|
||||
// We will toggle the PWM output when we reach this count.
|
||||
// #define PRST_PWM_FLIP_AT_COUNT PRST_PWM_MAX_COUNT / 2
|
||||
#define PRST_PWM_FLIP_AT_COUNT 8
|
||||
|
||||
static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
|
||||
|
||||
static nrf_pwm_values_common_t seq_values_[] = {PRST_PWM_FLIP_AT_COUNT};
|
||||
|
||||
static const nrf_pwm_sequence_t seq_ = {
|
||||
.values.p_common = seq_values_,
|
||||
.length = NRF_PWM_VALUES_LENGTH(seq_values_),
|
||||
.repeats = 0,
|
||||
.end_delay = 0};
|
||||
|
||||
void prst_pwm_init() {
|
||||
// We set the PWM pin as output so we can control its state after the PWM is
|
||||
// stopped. Without this, I'm seeing the PWM pin remaining high after stopped.
|
||||
nrf_gpio_pin_dir_set(PRST_PWM_PIN, NRF_GPIO_PIN_DIR_OUTPUT);
|
||||
|
||||
nrf_drv_pwm_config_t const config0 = {
|
||||
// We have to specify the state of the 4 channels. We only care about the
|
||||
// first one, so we set all others to not used.
|
||||
.output_pins =
|
||||
{
|
||||
PRST_PWM_PIN | NRF_DRV_PWM_PIN_INVERTED,
|
||||
NRF_DRV_PWM_PIN_NOT_USED,
|
||||
NRF_DRV_PWM_PIN_NOT_USED,
|
||||
NRF_DRV_PWM_PIN_NOT_USED,
|
||||
},
|
||||
.irq_priority = APP_IRQ_PRIORITY_LOWEST,
|
||||
// This is the hal PRESCALER
|
||||
.base_clock = NRF_PWM_CLK_16MHz,
|
||||
.count_mode = NRF_PWM_MODE_UP_AND_DOWN,
|
||||
// This is the hal COUNTERTOP.
|
||||
.top_value = PRST_PWM_MAX_COUNT,
|
||||
.load_mode = NRF_PWM_LOAD_COMMON,
|
||||
.step_mode = NRF_PWM_STEP_AUTO};
|
||||
APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
|
||||
}
|
||||
|
||||
void prst_pwm_start() {
|
||||
// Loop until stopped.
|
||||
APP_ERROR_CHECK(
|
||||
nrf_drv_pwm_simple_playback(&m_pwm0, &seq_, 1, NRF_DRV_PWM_FLAG_LOOP));
|
||||
}
|
||||
|
||||
void prst_pwm_stop() {
|
||||
nrf_drv_pwm_stop(&m_pwm0, /*wait_until_stopped=*/true);
|
||||
nrf_drv_pwm_uninit(&m_pwm0);
|
||||
nrf_gpio_pin_clear(PRST_PWM_PIN);
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#ifndef _PRST_PWM_H_
|
||||
#define _PRST_PWM_H_
|
||||
|
||||
void prst_pwm_init();
|
||||
|
||||
void prst_pwm_start();
|
||||
|
||||
void prst_pwm_stop();
|
||||
|
||||
#endif // _PRST_PWM_H_
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#include "prst/rtc.h"
|
||||
|
||||
#include <nrf_drv_rtc.h>
|
||||
#include <nrf_log.h>
|
||||
#include <nrf_log_ctrl.h>
|
||||
|
||||
#include "prst_config.h"
|
||||
|
||||
// RTC0 is used by softdevice, so we need to pick another instance.
|
||||
static const nrf_drv_rtc_t rtc_ = NRF_DRV_RTC_INSTANCE(2);
|
||||
static prst_rtc_callback_t callback_handler_ = NULL;
|
||||
|
||||
static void rtc_callback(nrf_drv_rtc_int_type_t int_type) {
|
||||
if (int_type == NRF_DRV_RTC_INT_COMPARE2) {
|
||||
if (callback_handler_ != NULL) {
|
||||
callback_handler_();
|
||||
}
|
||||
// Reset RTC2 counter.
|
||||
nrf_drv_rtc_counter_clear(&rtc_);
|
||||
// We need to re-enable the RTC2 interrupt after rest.
|
||||
nrf_drv_rtc_int_enable(&rtc_, NRF_RTC_INT_COMPARE2_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
void prst_rtc_set_callback(prst_rtc_callback_t cb) { callback_handler_ = cb; }
|
||||
|
||||
void prst_rtc_init() {
|
||||
uint32_t err_code;
|
||||
nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;
|
||||
config.prescaler = 4095;
|
||||
err_code = nrf_drv_rtc_init(&rtc_, &config, rtc_callback);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
// Disable events we're not interested in so they don't trigger interrupts.
|
||||
nrf_drv_rtc_tick_disable(&rtc_);
|
||||
nrf_drv_rtc_overflow_disable(&rtc_);
|
||||
|
||||
// Make sure we're counting from 0.
|
||||
nrf_drv_rtc_counter_clear(&rtc_);
|
||||
|
||||
// Set compare channel to trigger interrupt after specified time.
|
||||
err_code = nrf_drv_rtc_cc_set(&rtc_, 2, PRST_DEEP_SLEEP_IN_SECONDS * 8, true);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
// Power on RTC instance.
|
||||
nrf_drv_rtc_enable(&rtc_);
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#ifndef _PRST_RTC_H_
|
||||
#define _PRST_RTC_H_
|
||||
|
||||
typedef void (*prst_rtc_callback_t)(void);
|
||||
|
||||
void prst_rtc_set_callback(prst_rtc_callback_t cb);
|
||||
|
||||
void prst_rtc_init();
|
||||
|
||||
#endif // _PRST_RTC_H_
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
#include "prst/shtc3.h"
|
||||
|
||||
#include <app_error.h>
|
||||
#include <nrf_delay.h>
|
||||
#include <nrf_drv_twi.h>
|
||||
#include <nrf_log.h>
|
||||
#include <nrf_log_ctrl.h>
|
||||
|
||||
#include "prst_config.h"
|
||||
|
||||
static const nrf_drv_twi_t twi_ = NRF_DRV_TWI_INSTANCE(0);
|
||||
static nrf_drv_twi_config_t twi_config_ = NRF_DRV_TWI_DEFAULT_CONFIG;
|
||||
|
||||
static uint8_t buff[6];
|
||||
|
||||
static void write_cmd(uint16_t command) {
|
||||
uint8_t cmd[2];
|
||||
cmd[0] = command >> 8;
|
||||
cmd[1] = command & 0xff;
|
||||
APP_ERROR_CHECK(nrf_drv_twi_tx(&twi_, PRST_SHTC3_ADDR, cmd, 2,
|
||||
/*no_stop=*/false));
|
||||
}
|
||||
|
||||
void prst_shtc3_init() {
|
||||
twi_config_.scl = PRST_SHT3C_SCL_PIN;
|
||||
twi_config_.sda = PRST_SHT3C_SDA_PIN;
|
||||
twi_config_.frequency = NRF_TWI_FREQ_100K;
|
||||
}
|
||||
|
||||
prst_shtc3_read_t prst_shtc3_read() {
|
||||
APP_ERROR_CHECK(nrf_drv_twi_init(&twi_, &twi_config_, NULL, NULL));
|
||||
nrf_drv_twi_enable(&twi_);
|
||||
|
||||
// Wake the sensor up.
|
||||
write_cmd(PRST_SHTC3_CMD_WAKEUP);
|
||||
nrf_delay_ms(1);
|
||||
|
||||
// Request measurement.
|
||||
write_cmd(PRST_SHTC3_CMD_MEASURE_TFIRST_NORMAL);
|
||||
|
||||
// Reading in normal (not low power) mode can take up to 12.1 ms, according to
|
||||
// the datasheet.
|
||||
nrf_delay_ms(15);
|
||||
|
||||
// Read temp and humidity.
|
||||
while (nrf_drv_twi_rx(&twi_, PRST_SHTC3_ADDR, buff, 6) != 0) {
|
||||
nrf_delay_ms(10);
|
||||
}
|
||||
// Put the sensor in sleep mode.
|
||||
write_cmd(PRST_SHTC3_CMD_SLEEP);
|
||||
|
||||
// Uninit i2c.
|
||||
nrf_drv_twi_uninit(&twi_);
|
||||
|
||||
// TODO(rbaron): verify the CRC of the measurements. The function is described
|
||||
// in the datasheet.
|
||||
|
||||
float temp_c = -45 + 175 * ((float)((buff[0] << 8) | buff[1])) / (1 << 16);
|
||||
uint16_t humi = (buff[3] << 8) | buff[4];
|
||||
|
||||
prst_shtc3_read_t ret = {.temp_celsius = temp_c, .humidity = humi};
|
||||
|
||||
#if PRST_SHT3C_DEBUG
|
||||
NRF_LOG_INFO("[sht3c] Read temp: " NRF_LOG_FLOAT_MARKER " oC",
|
||||
NRF_LOG_FLOAT((float)ret.temp_celsius));
|
||||
NRF_LOG_INFO("[sht3c] Read humi: " NRF_LOG_FLOAT_MARKER " %%",
|
||||
NRF_LOG_FLOAT(100.0 * ret.humidity / 0xffff));
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef _PRST_SHT3C_H_
|
||||
#define _PRST_SHT3C_H_
|
||||
|
||||
#include <nrf_gpio.h>
|
||||
|
||||
#define PRST_SHT3C_SDA_PIN NRF_GPIO_PIN_MAP(0, 24)
|
||||
#define PRST_SHT3C_SCL_PIN NRF_GPIO_PIN_MAP(0, 13)
|
||||
|
||||
// Values from the SHTC3 datasheet.
|
||||
#define PRST_SHTC3_ADDR 0x70
|
||||
#define PRST_SHTC3_CMD_SLEEP 0xb098
|
||||
#define PRST_SHTC3_CMD_WAKEUP 0x3517
|
||||
#define PRST_SHTC3_CMD_MEASURE_TFIRST_LOW_POWER 0x609c
|
||||
#define PRST_SHTC3_CMD_MEASURE_TFIRST_NORMAL 0x7866
|
||||
|
||||
typedef struct prst_shtc3_values {
|
||||
// Temperature in degrees Celsius.
|
||||
float temp_celsius;
|
||||
// Relative humidity, from 0 to 2^16.
|
||||
uint16_t humidity;
|
||||
} prst_shtc3_read_t;
|
||||
|
||||
void prst_shtc3_init();
|
||||
prst_shtc3_read_t prst_shtc3_read();
|
||||
|
||||
#endif // _PRST_SHT3C_H_
|
||||
14
code/nrf-connect/prstlib/CMakeLists.txt
Normal file
14
code/nrf-connect/prstlib/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
|
||||
add_library(prstlib STATIC
|
||||
src/adc.c
|
||||
src/button.c
|
||||
src/led.c
|
||||
src/sensors.c
|
||||
src/shtc3.c
|
||||
)
|
||||
|
||||
target_include_directories(prstlib PRIVATE include)
|
||||
target_link_libraries(prstlib PUBLIC zephyr_interface kernel)
|
||||
3
code/nrf-connect/prstlib/Kconfig
Normal file
3
code/nrf-connect/prstlib/Kconfig
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module = PRSTLIB
|
||||
module-str = prstlib
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
if BOARD_BPARASITE_NRF52833
|
||||
|
||||
config BOARD_ENABLE_DCDC
|
||||
bool "DCDC mode"
|
||||
select SOC_DCDC_NRF52X
|
||||
default y
|
||||
|
||||
endif # BOARD_BPARASITE_NRF52833
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
config BOARD_BPARASITE_NRF52833
|
||||
bool "b-parasite nRF52833 board"
|
||||
depends on SOC_NRF52833_QIAA
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
if BOARD_BPARASITE_NRF52833
|
||||
|
||||
config BOARD
|
||||
default "bparasite_nrf52833"
|
||||
|
||||
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
|
||||
|
||||
endif # BOARD_BPARASITE_NRF52833
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
board_runner_args(jlink "--device=nRF52833_xxAA" "--speed=4000")
|
||||
board_runner_args(pyocd "--target=nrf52833" "--frequency=4000000")
|
||||
# set(OPENOCD_NRF5_SUBFAMILY "nrf52")
|
||||
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/openocd-nrf5.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/blackmagicprobe.board.cmake)
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
&pinctrl {
|
||||
uart0_default: uart0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 25)>,
|
||||
<NRF_PSEL(UART_RX, 0, 23)>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0_sleep: uart0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 25)>,
|
||||
<NRF_PSEL(UART_RX, 0, 23)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* Configure pwm0 instance to use pin 5. */
|
||||
pwm0_default: pwm0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(PWM_OUT0, 0, 5)>;
|
||||
nordic,invert;
|
||||
};
|
||||
};
|
||||
|
||||
pwm0_sleep: pwm0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(PWM_OUT0, 0, 5)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* Configure i2c0 instance to use pins 24 (SDA) & 13 (SCL). */
|
||||
i2c0_default: i2c0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(TWIM_SDA, 0, 24)>,
|
||||
<NRF_PSEL(TWIM_SCL, 0, 13)>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_sleep: i2c0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(TWIM_SDA, 0, 24)>,
|
||||
<NRF_PSEL(TWIM_SCL, 0, 13)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
/dts-v1/;
|
||||
#include <nordic/nrf52833_qiaa.dtsi>
|
||||
#include "bparasite_nrf52833-pinctrl.dtsi"
|
||||
|
||||
/ {
|
||||
model = "A soil moisture sensor based on Nordic's nRF52833";
|
||||
compatible = "rbaron,bparasite_nrf52833";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,code-partition = &slot0_partition;
|
||||
zephyr,ieee802154 = &ieee802154;
|
||||
};
|
||||
|
||||
zephyr,user {
|
||||
io-channels = <&adc 0>, <&adc 2>;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
led0: led_0 {
|
||||
// P0.28.
|
||||
gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
buttons {
|
||||
compatible = "gpio-keys";
|
||||
button0: button_0 {
|
||||
// P0.30.
|
||||
gpios = <&gpio0 30 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||
label = "Push button SW1";
|
||||
};
|
||||
};
|
||||
|
||||
soil_pwm: soil_pwm {
|
||||
compatible = "pwm-fixed";
|
||||
pwms = <&pwm0 0 PWM_USEC(2) PWM_POLARITY_NORMAL>;
|
||||
pulse = <PWM_USEC(1)>;
|
||||
};
|
||||
|
||||
soil_calibration_coeffs: soil_calibration_coeffs {
|
||||
compatible = "soil-calibration-coeffs";
|
||||
dry = <306000 101000 (-11700)>;
|
||||
wet = <19000 (-4980) 3420>;
|
||||
};
|
||||
|
||||
ctrl {
|
||||
compatible = "gpio-keys";
|
||||
fast_disch: fast_disch {
|
||||
// P0.25.
|
||||
gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>;
|
||||
label = "Fast discharge circuitry";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&uicr {
|
||||
gpio-as-nreset;
|
||||
};
|
||||
|
||||
&gpiote {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
// For low-power EDGE interrupts.
|
||||
// See github.com/zephyrproject-rtos/zephyr/issues/28499.
|
||||
sense-edge-mask = <0xffffffff>;
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&ieee802154 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
compatible = "nordic,nrf-uart";
|
||||
status = "disabled";
|
||||
current-speed = <115200>;
|
||||
pinctrl-0 = <&uart0_default>;
|
||||
pinctrl-1 = <&uart0_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
compatible = "nordic,nrf-twi";
|
||||
status = "okay";
|
||||
pinctrl-0 = <&i2c0_default>;
|
||||
pinctrl-1 = <&i2c0_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
|
||||
shtc3: shtc3@70 {
|
||||
compatible = "i2c-device";
|
||||
reg = <0x70>;
|
||||
label = "SHTC3";
|
||||
};
|
||||
};
|
||||
|
||||
&pwm0 {
|
||||
status = "okay";
|
||||
pinctrl-0 = <&pwm0_default>;
|
||||
pinctrl-1 = <&pwm0_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
};
|
||||
|
||||
&adc {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "ok";
|
||||
|
||||
// Soil.
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_VDD_1_4";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
// P0.03.
|
||||
zephyr,input-positive = <NRF_SAADC_AIN1>;
|
||||
zephyr,resolution = <10>;
|
||||
|
||||
};
|
||||
|
||||
// Battery.
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
zephyr,input-positive = <NRF_SAADC_VDD>;
|
||||
zephyr,resolution = <10>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
&flash0 {
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
boot_partition: partition@0 {
|
||||
label = "mcuboot";
|
||||
reg = <0x000000000 0xC000>;
|
||||
};
|
||||
slot0_partition: partition@c000 {
|
||||
label = "image-0";
|
||||
reg = <0x0000C000 0x32000>;
|
||||
};
|
||||
slot1_partition: partition@3e000 {
|
||||
label = "image-1";
|
||||
reg = <0x0003E000 0x32000>;
|
||||
};
|
||||
scratch_partition: partition@70000 {
|
||||
label = "image-scratch";
|
||||
reg = <0x00070000 0xA000>;
|
||||
};
|
||||
storage_partition: partition@7a000 {
|
||||
label = "storage";
|
||||
reg = <0x0007A000 0x00006000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CONFIG_BOARD_REVISION="1.1.0"
|
||||
CONFIG_BOARD_REVISION_CODE=2
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
/ {
|
||||
// Light dependant resistor.
|
||||
ldr: ldr {
|
||||
compatible = "voltage-divider";
|
||||
output-ohms = <10000>;
|
||||
io-channels = <&adc 1>;
|
||||
};
|
||||
|
||||
ctrl {
|
||||
compatible = "gpio-keys";
|
||||
ldr_enable: ldr_enable {
|
||||
// P0.29.
|
||||
gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>;
|
||||
label = "LDR supply";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&adc {
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
// P0.02.
|
||||
zephyr,input-positive = <NRF_SAADC_AIN0>;
|
||||
zephyr,resolution = <10>;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CONFIG_BOARD_REVISION="1.2.0"
|
||||
CONFIG_BOARD_REVISION_CODE=3
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
/ {
|
||||
photo_transistor: photo_transistor {
|
||||
compatible = "voltage-divider";
|
||||
output-ohms = <470>;
|
||||
io-channels = <&adc 1>;
|
||||
};
|
||||
|
||||
ctrl {
|
||||
compatible = "gpio-keys";
|
||||
photo_transistor_enable: photo_transistor_enable {
|
||||
// P0.29.
|
||||
gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>;
|
||||
label = "Phototransistor supply";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&adc {
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
// P0.02.
|
||||
zephyr,input-positive = <NRF_SAADC_AIN0>;
|
||||
zephyr,resolution = <10>;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CONFIG_BOARD_REVISION="2.0.0"
|
||||
CONFIG_BOARD_REVISION_CODE=4
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
/ {
|
||||
photo_transistor: photo_transistor {
|
||||
compatible = "voltage-divider";
|
||||
output-ohms = <470>;
|
||||
io-channels = <&adc 1>;
|
||||
};
|
||||
|
||||
soil_calibration_coeffs: soil_calibration_coeffs {
|
||||
compatible = "soil-calibration-coeffs";
|
||||
dry = <334000 110000 (-15300)>;
|
||||
wet = <299000 (-83100) 11200>;
|
||||
};
|
||||
|
||||
ctrl {
|
||||
compatible = "gpio-keys";
|
||||
photo_transistor_enable: photo_transistor_enable {
|
||||
// P0.29.
|
||||
gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>;
|
||||
label = "Phototransistor supply";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&adc {
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
// P0.02.
|
||||
zephyr,input-positive = <NRF_SAADC_AIN0>;
|
||||
zephyr,resolution = <10>;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
CONFIG_SOC_SERIES_NRF52X=y
|
||||
CONFIG_SOC_NRF52833_QIAA=y
|
||||
CONFIG_BOARD_BPARASITE_NRF52833=y
|
||||
|
||||
# Enable MPU
|
||||
CONFIG_ARM_MPU=y
|
||||
|
||||
# Enable RTT
|
||||
CONFIG_USE_SEGGER_RTT=y
|
||||
|
||||
# enable GPIO
|
||||
CONFIG_GPIO=y
|
||||
|
||||
# enable console
|
||||
CONFIG_CONSOLE=y
|
||||
CONFIG_RTT_CONSOLE=y
|
||||
|
||||
CONFIG_PINCTRL=y
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Copyright (c) 2022 Nordic Semiconductor
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Suppress "unique_unit_address_if_enabled" to handle the following overlaps:
|
||||
# - power@40000000 & clock@40000000 & bprot@40000000
|
||||
# - acl@4001e000 & flash-controller@4001e000
|
||||
list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled")
|
||||
|
|
@ -0,0 +1 @@
|
|||
board_check_revision(FORMAT MAJOR.MINOR.PATCH)
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
if BOARD_BPARASITE_NRF52840
|
||||
|
||||
config BOARD_ENABLE_DCDC
|
||||
bool "DCDC mode"
|
||||
select SOC_DCDC_NRF52X
|
||||
default y
|
||||
|
||||
config BOARD_ENABLE_DCDC_HV
|
||||
bool "High Voltage DCDC converter"
|
||||
select SOC_DCDC_NRF52X_HV
|
||||
default y
|
||||
|
||||
endif # BOARD_BPARASITE_NRF52840
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
config BOARD_BPARASITE_NRF52840
|
||||
bool "b-parasite nRF52840 board"
|
||||
depends on SOC_NRF52840_QIAA
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
if BOARD_BPARASITE_NRF52840
|
||||
|
||||
config BOARD
|
||||
default "bparasite_nrf52840"
|
||||
|
||||
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
|
||||
|
||||
endif # BOARD_BPARASITE_NRF52840
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
board_runner_args(jlink "--device=nRF52840_xxAA" "--speed=4000")
|
||||
board_runner_args(pyocd "--target=nrf52840" "--frequency=4000000")
|
||||
# set(OPENOCD_NRF5_SUBFAMILY "nrf52")
|
||||
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/openocd-nrf5.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/blackmagicprobe.board.cmake)
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
&pinctrl {
|
||||
uart0_default: uart0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 25)>,
|
||||
<NRF_PSEL(UART_RX, 0, 23)>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0_sleep: uart0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(UART_TX, 0, 25)>,
|
||||
<NRF_PSEL(UART_RX, 0, 23)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* Configure pwm0 instance to use pin 5. */
|
||||
pwm0_default: pwm0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(PWM_OUT0, 0, 5)>;
|
||||
nordic,invert;
|
||||
};
|
||||
};
|
||||
|
||||
pwm0_sleep: pwm0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(PWM_OUT0, 0, 5)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* Configure i2c0 instance to use pins 24 (SDA) & 13 (SCL). */
|
||||
i2c0_default: i2c0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(TWIM_SDA, 0, 24)>,
|
||||
<NRF_PSEL(TWIM_SCL, 0, 13)>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_sleep: i2c0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(TWIM_SDA, 0, 24)>,
|
||||
<NRF_PSEL(TWIM_SCL, 0, 13)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
/dts-v1/;
|
||||
#include <nordic/nrf52840_qiaa.dtsi>
|
||||
#include "bparasite_nrf52840-pinctrl.dtsi"
|
||||
|
||||
/ {
|
||||
model = "A soil moisture sensor based on Nordic's nRF52840";
|
||||
compatible = "rbaron,bparasite_nrf52840";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,code-partition = &slot0_partition;
|
||||
zephyr,ieee802154 = &ieee802154;
|
||||
};
|
||||
|
||||
zephyr,user {
|
||||
io-channels = <&adc 0>, <&adc 2>;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
led0: led_0 {
|
||||
// P0.28.
|
||||
gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
buttons {
|
||||
compatible = "gpio-keys";
|
||||
button0: button_0 {
|
||||
// P0.30.
|
||||
gpios = <&gpio0 30 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||
label = "Push button SW1";
|
||||
};
|
||||
};
|
||||
|
||||
soil_pwm: soil_pwm {
|
||||
compatible = "pwm-fixed";
|
||||
pwms = <&pwm0 0 PWM_USEC(2) PWM_POLARITY_NORMAL>;
|
||||
pulse = <PWM_USEC(1)>;
|
||||
};
|
||||
|
||||
soil_calibration_coeffs: soil_calibration_coeffs {
|
||||
compatible = "soil-calibration-coeffs";
|
||||
dry = <306000 101000 (-11700)>;
|
||||
wet = <19000 (-4980) 3420>;
|
||||
};
|
||||
|
||||
ctrl {
|
||||
compatible = "gpio-keys";
|
||||
fast_disch: fast_disch {
|
||||
// P1.10.
|
||||
gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
|
||||
label = "Fast discharge circuitry";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&uicr {
|
||||
gpio-as-nreset;
|
||||
};
|
||||
|
||||
&gpiote {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpio0 {
|
||||
status = "okay";
|
||||
// For low-power EDGE interrupts.
|
||||
// github.com/zephyrproject-rtos/zephyr/issues/28499
|
||||
sense-edge-mask = <0xffffffff>;
|
||||
};
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&ieee802154 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart0 {
|
||||
compatible = "nordic,nrf-uart";
|
||||
status = "disabled";
|
||||
current-speed = <115200>;
|
||||
pinctrl-0 = <&uart0_default>;
|
||||
pinctrl-1 = <&uart0_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
compatible = "nordic,nrf-twi";
|
||||
status = "okay";
|
||||
pinctrl-0 = <&i2c0_default>;
|
||||
pinctrl-1 = <&i2c0_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
|
||||
shtc3: shtc3@70 {
|
||||
compatible = "i2c-device";
|
||||
reg = <0x70>;
|
||||
label = "SHTC3";
|
||||
};
|
||||
};
|
||||
|
||||
&pwm0 {
|
||||
status = "okay";
|
||||
pinctrl-0 = <&pwm0_default>;
|
||||
pinctrl-1 = <&pwm0_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
};
|
||||
|
||||
&adc {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "ok";
|
||||
|
||||
// Soil.
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_VDD_1_4";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
// P0.03.
|
||||
zephyr,input-positive = <NRF_SAADC_AIN1>;
|
||||
zephyr,resolution = <10>;
|
||||
|
||||
};
|
||||
|
||||
// Battery.
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
zephyr,input-positive = <NRF_SAADC_VDD>;
|
||||
zephyr,resolution = <10>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
&flash0 {
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
boot_partition: partition@0 {
|
||||
label = "mcuboot";
|
||||
reg = <0x00000000 0x0000C000>;
|
||||
};
|
||||
slot0_partition: partition@c000 {
|
||||
label = "image-0";
|
||||
reg = <0x0000C000 0x00067000>;
|
||||
};
|
||||
slot1_partition: partition@73000 {
|
||||
label = "image-1";
|
||||
reg = <0x00073000 0x00067000>;
|
||||
};
|
||||
scratch_partition: partition@da000 {
|
||||
label = "image-scratch";
|
||||
reg = <0x000da000 0x0001e000>;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
label = "storage";
|
||||
reg = <0x000f8000 0x00008000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CONFIG_BOARD_REVISION="1.1.0"
|
||||
CONFIG_BOARD_REVISION_CODE=2
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
/ {
|
||||
// Light dependant resistor.
|
||||
ldr: ldr {
|
||||
compatible = "voltage-divider";
|
||||
output-ohms = <10000>;
|
||||
io-channels = <&adc 1>;
|
||||
};
|
||||
|
||||
ctrl {
|
||||
compatible = "gpio-keys";
|
||||
ldr_enable: ldr_enable {
|
||||
// P0.29.
|
||||
gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>;
|
||||
label = "LDR supply";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&adc {
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
// P0.02.
|
||||
zephyr,input-positive = <NRF_SAADC_AIN0>;
|
||||
zephyr,resolution = <10>;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CONFIG_BOARD_REVISION="1.2.0"
|
||||
CONFIG_BOARD_REVISION_CODE=3
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
/ {
|
||||
photo_transistor: photo_transistor {
|
||||
compatible = "voltage-divider";
|
||||
output-ohms = <470>;
|
||||
io-channels = <&adc 1>;
|
||||
};
|
||||
|
||||
ctrl {
|
||||
compatible = "gpio-keys";
|
||||
photo_transistor_enable: photo_transistor_enable {
|
||||
// P0.29.
|
||||
gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>;
|
||||
label = "Phototransistor supply";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&adc {
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
// P0.02.
|
||||
zephyr,input-positive = <NRF_SAADC_AIN0>;
|
||||
zephyr,resolution = <10>;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
CONFIG_BOARD_REVISION="2.0.0"
|
||||
CONFIG_BOARD_REVISION_CODE=4
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
/ {
|
||||
photo_transistor: photo_transistor {
|
||||
compatible = "voltage-divider";
|
||||
output-ohms = <470>;
|
||||
io-channels = <&adc 1>;
|
||||
};
|
||||
|
||||
soil_calibration_coeffs: soil_calibration_coeffs {
|
||||
compatible = "soil-calibration-coeffs";
|
||||
dry = <334000 110000 (-15300)>;
|
||||
wet = <299000 (-83100) 11200>;
|
||||
};
|
||||
|
||||
ctrl {
|
||||
compatible = "gpio-keys";
|
||||
photo_transistor_enable: photo_transistor_enable {
|
||||
// P0.29.
|
||||
gpios = <&gpio0 29 GPIO_ACTIVE_HIGH>;
|
||||
label = "Phototransistor supply";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&adc {
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
// P0.02.
|
||||
zephyr,input-positive = <NRF_SAADC_AIN0>;
|
||||
zephyr,resolution = <10>;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
CONFIG_SOC_SERIES_NRF52X=y
|
||||
CONFIG_SOC_NRF52840_QIAA=y
|
||||
CONFIG_BOARD_BPARASITE_NRF52840=y
|
||||
|
||||
# Enable MPU
|
||||
CONFIG_ARM_MPU=y
|
||||
|
||||
# Enable RTT
|
||||
CONFIG_USE_SEGGER_RTT=y
|
||||
|
||||
# enable GPIO
|
||||
CONFIG_GPIO=y
|
||||
|
||||
# enable console
|
||||
CONFIG_CONSOLE=y
|
||||
CONFIG_RTT_CONSOLE=y
|
||||
|
||||
CONFIG_PINCTRL=y
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Copyright (c) 2022 Nordic Semiconductor
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Suppress "unique_unit_address_if_enabled" to handle the following overlaps:
|
||||
# - power@40000000 & clock@40000000 & bprot@40000000
|
||||
# - acl@4001e000 & flash-controller@4001e000
|
||||
list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled")
|
||||
|
|
@ -0,0 +1 @@
|
|||
board_check_revision(FORMAT MAJOR.MINOR.PATCH)
|
||||
17
code/nrf-connect/prstlib/dts/bindings/pwm-fixed.yaml
Normal file
17
code/nrf-connect/prstlib/dts/bindings/pwm-fixed.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
description: A fixed frequency & pulse PWM.
|
||||
|
||||
compatible: pwm-fixed
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
pwms:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: the PWM spec.
|
||||
|
||||
pulse:
|
||||
required: true
|
||||
type: int
|
||||
description: The period of the PWM pulse.
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
description: >
|
||||
Calibration coefficients for the soil sensing circuit. We have two polynomials whose input variable
|
||||
is the battery voltage in Volts. The evaluated polynomial corresponds to the estimated ADC value,
|
||||
assumed to be 10 bits, so in [0, 1024). The two polynomials are:
|
||||
dry: For estimation of the fully dry state, given the battery voltage
|
||||
wet: For estimation of the fully dry state, given the battery voltage
|
||||
|
||||
Note that each poly coefficient is specified as 1000 times its real values. This is because we can't
|
||||
directly represent floating points in devicetree. The final evaluated value should be divided by
|
||||
1000.0f in code.
|
||||
|
||||
compatible: soil-calibration-coeffs
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
dry:
|
||||
type: array
|
||||
required: true
|
||||
description: The coefficients * 1000, as integers. 0th degree first, then 1st and 2nd last.
|
||||
wet:
|
||||
type: array
|
||||
required: true
|
||||
description: The coefficients * 1000, as integers. 0th degree first, then 1st and 2nd last.
|
||||
39
code/nrf-connect/prstlib/include/prstlib/adc.h
Normal file
39
code/nrf-connect/prstlib/include/prstlib/adc.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef _PRST_ADC_H_
|
||||
#define _PRST_ADC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
int16_t raw;
|
||||
int32_t millivolts;
|
||||
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).
|
||||
uint16_t relative;
|
||||
// In [0, 1.0].
|
||||
float percentage;
|
||||
} prst_adc_soil_moisture_t;
|
||||
|
||||
typedef struct prst_adc_photo_sensor {
|
||||
prst_adc_read_t adc_read;
|
||||
// Value in lux.
|
||||
uint16_t brightness;
|
||||
} prst_adc_photo_sensor_t;
|
||||
|
||||
int prst_adc_init();
|
||||
|
||||
int prst_adc_batt_read(prst_batt_t* out);
|
||||
|
||||
int prst_adc_soil_read(float battery_voltage, prst_adc_soil_moisture_t* out);
|
||||
|
||||
int prst_adc_photo_read(float battery_voltage, prst_adc_photo_sensor_t* out);
|
||||
|
||||
#endif // _PRST_ADC_H_
|
||||
24
code/nrf-connect/prstlib/include/prstlib/button.h
Normal file
24
code/nrf-connect/prstlib/include/prstlib/button.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef _PRST_BUTTON_H_
|
||||
#define _PRST_BUTTON_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum {
|
||||
PRST_BUTTON_SW1 = 0,
|
||||
} prst_button_t;
|
||||
|
||||
typedef void (*prst_button_callback_t)(prst_button_t button, bool is_active);
|
||||
|
||||
// Inits button driver.
|
||||
int prst_button_init();
|
||||
|
||||
// Configures ISR and calls callback on debounced button press/release.
|
||||
int prst_button_register_callback(prst_button_callback_t callback);
|
||||
|
||||
// Returns:
|
||||
// 1 if button is active
|
||||
// 0 if button is inactive
|
||||
// -1 on error
|
||||
int prst_button_poll(prst_button_t prst_button);
|
||||
|
||||
#endif // _PRST_BUTTON_H_
|
||||
38
code/nrf-connect/prstlib/include/prstlib/led.h
Normal file
38
code/nrf-connect/prstlib/include/prstlib/led.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef _PRST_LED_H_
|
||||
#define _PRST_LED_H_
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "prstlib/macros.h"
|
||||
|
||||
#define PRST_LED_FLASH_PERIOD_MS 400
|
||||
|
||||
extern struct gpio_dt_spec led;
|
||||
|
||||
int prst_led_init();
|
||||
|
||||
static inline int prst_led_on() {
|
||||
return gpio_pin_set_dt(&led, 1);
|
||||
}
|
||||
|
||||
static inline int prst_led_off() {
|
||||
return gpio_pin_set_dt(&led, 0);
|
||||
}
|
||||
|
||||
static inline int prst_led_toggle() {
|
||||
return gpio_pin_toggle_dt(&led);
|
||||
}
|
||||
|
||||
static inline int prst_led_flash(int times) {
|
||||
LOG_MODULE_DECLARE(led, LOG_LEVEL_DBG);
|
||||
RET_IF_ERR(prst_led_off());
|
||||
for (int i = 0; i < 2 * times; i++) {
|
||||
RET_IF_ERR(prst_led_toggle());
|
||||
k_msleep(PRST_LED_FLASH_PERIOD_MS / 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // _PRST_LED_H_
|
||||
24
code/nrf-connect/prstlib/include/prstlib/macros.h
Normal file
24
code/nrf-connect/prstlib/include/prstlib/macros.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef _PRST_MACROS_H_
|
||||
#define _PRST_MACROS_H_
|
||||
|
||||
#define PRST_STRINGIFY(x) #x
|
||||
#define PRST_TO_STRING(x) PRST_STRINGIFY(x)
|
||||
#define PRST_LOCATION __FILE__ ":" PRST_TO_STRING(__LINE__)
|
||||
|
||||
#define RET_IF_ERR_MSG(expr, msg) \
|
||||
{ \
|
||||
int err = (expr); \
|
||||
if (err) { \
|
||||
LOG_ERR("Error %d: " msg " in " PRST_LOCATION, err); \
|
||||
return err; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define RET_IF_ERR(expr) RET_IF_ERR_MSG(expr, "")
|
||||
|
||||
// Checks that expr evaluates to true, otherwise return 1.
|
||||
#define RET_CHECK(expr, msg) RET_IF_ERR_MSG(!(expr), msg)
|
||||
|
||||
#define UNUSED_OK(expr) (void)expr;
|
||||
|
||||
#endif // _PRST_MACROS_H_
|
||||
16
code/nrf-connect/prstlib/include/prstlib/sensors.h
Normal file
16
code/nrf-connect/prstlib/include/prstlib/sensors.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef _PRST_DATA_H_
|
||||
#define _PRST_DATA_H_
|
||||
|
||||
#include "prstlib/adc.h"
|
||||
#include "prstlib/shtc3.h"
|
||||
|
||||
typedef struct {
|
||||
prst_adc_soil_moisture_t soil;
|
||||
prst_adc_photo_sensor_t photo;
|
||||
prst_batt_t batt;
|
||||
prst_shtc3_read_t shtc3;
|
||||
} prst_sensors_t;
|
||||
|
||||
int prst_sensors_read_all(prst_sensors_t *out);
|
||||
|
||||
#endif // _PRST_DATA_H_
|
||||
20
code/nrf-connect/prstlib/include/prstlib/shtc3.h
Normal file
20
code/nrf-connect/prstlib/include/prstlib/shtc3.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef _PRST_SHT3C_H_
|
||||
#define _PRST_SHT3C_H_
|
||||
|
||||
// Values from the SHTC3 datasheet.
|
||||
#define PRST_SHTC3_ADDR 0x70
|
||||
#define PRST_SHTC3_CMD_SLEEP 0xb098
|
||||
#define PRST_SHTC3_CMD_WAKEUP 0x3517
|
||||
#define PRST_SHTC3_CMD_MEASURE_TFIRST_LOW_POWER 0x609c
|
||||
#define PRST_SHTC3_CMD_MEASURE_TFIRST_NORMAL 0x7866
|
||||
|
||||
typedef struct {
|
||||
// Temperature in Celcius.
|
||||
float temp_c;
|
||||
// Relative humidity in [0, 1.0].
|
||||
float rel_humi;
|
||||
} prst_shtc3_read_t;
|
||||
|
||||
int prst_shtc3_read(prst_shtc3_read_t *out);
|
||||
|
||||
#endif // _PRST_SHT3C_H_
|
||||
209
code/nrf-connect/prstlib/src/adc.c
Normal file
209
code/nrf-connect/prstlib/src/adc.c
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
#include "prstlib/adc.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/adc.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/pwm.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "prstlib/macros.h"
|
||||
|
||||
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 =
|
||||
PWM_DT_SPEC_GET(DT_NODELABEL(soil_pwm));
|
||||
static const uint32_t pulse = DT_PROP(DT_NODELABEL(soil_pwm), pulse);
|
||||
|
||||
// Calibration coefficients for the soil sensing circuit.
|
||||
static const int dry_coeffs[3] = DT_PROP(DT_NODELABEL(soil_calibration_coeffs), dry);
|
||||
static const int wet_coeffs[3] = DT_PROP(DT_NODELABEL(soil_calibration_coeffs), wet);
|
||||
|
||||
struct gpio_dt_spec fast_disch_dt =
|
||||
GPIO_DT_SPEC_GET(DT_NODELABEL(fast_disch), gpios);
|
||||
|
||||
// Shared buffer and adc_sequennce.
|
||||
static int16_t buf;
|
||||
static struct adc_sequence sequence = {
|
||||
.buffer = &buf,
|
||||
.buffer_size = sizeof(buf),
|
||||
};
|
||||
|
||||
static const struct adc_dt_spec adc_soil_spec =
|
||||
ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0);
|
||||
|
||||
static const struct adc_dt_spec adc_batt_spec =
|
||||
ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 1);
|
||||
|
||||
#if DT_NODE_EXISTS(DT_NODELABEL(photo_transistor))
|
||||
|
||||
static const struct adc_dt_spec adc_photo_transistor_spec =
|
||||
ADC_DT_SPEC_GET(DT_NODELABEL(photo_transistor));
|
||||
struct gpio_dt_spec photo_transistor_enable_dt =
|
||||
GPIO_DT_SPEC_GET(DT_NODELABEL(photo_transistor_enable), gpios);
|
||||
|
||||
#elif DT_NODE_EXISTS(DT_NODELABEL(ldr))
|
||||
|
||||
static const struct adc_dt_spec adc_ldr_spec =
|
||||
ADC_DT_SPEC_GET(DT_NODELABEL(ldr));
|
||||
struct gpio_dt_spec ldr_enable_dt =
|
||||
GPIO_DT_SPEC_GET(DT_NODELABEL(ldr_enable), gpios);
|
||||
|
||||
#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 eval_poly(const int coeffs[3], float x) {
|
||||
// The coefficients are specified times 1000, as a workaround the lack of support for floating
|
||||
// points in devicetree bindings.
|
||||
return (coeffs[0] + coeffs[1] * x + coeffs[2] * x * x) / 1000.0f;
|
||||
}
|
||||
|
||||
static inline float get_soil_moisture_percent(float battery_voltage,
|
||||
int16_t raw_adc_output) {
|
||||
const float x = battery_voltage;
|
||||
const float dry = eval_poly(dry_coeffs, x);
|
||||
const float wet = eval_poly(wet_coeffs, x);
|
||||
const float percent = (raw_adc_output - dry) / (wet - dry);
|
||||
LOG_DBG("Read soil moisture 2: %.2f | Raw %u | Batt: %.2f | Dry: %.2f | Wet: %.2f",
|
||||
100.0f * percent, raw_adc_output, x, dry, wet);
|
||||
return percent;
|
||||
}
|
||||
|
||||
static int read_adc_spec(const struct adc_dt_spec* spec, prst_adc_read_t* out) {
|
||||
RET_IF_ERR(adc_sequence_init_dt(spec, &sequence));
|
||||
|
||||
RET_IF_ERR(adc_read(spec->dev, &sequence));
|
||||
|
||||
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;
|
||||
out->voltage = val_mv / 1000.0f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prst_adc_init() {
|
||||
RET_IF_ERR(adc_channel_setup_dt(&adc_soil_spec));
|
||||
RET_IF_ERR(adc_channel_setup_dt(&adc_batt_spec));
|
||||
|
||||
RET_IF_ERR(!device_is_ready(fast_disch_dt.port));
|
||||
RET_IF_ERR(gpio_pin_configure_dt(&fast_disch_dt, GPIO_OUTPUT));
|
||||
|
||||
#if DT_NODE_EXISTS(DT_NODELABEL(photo_transistor))
|
||||
RET_IF_ERR(adc_channel_setup_dt(&adc_photo_transistor_spec));
|
||||
RET_IF_ERR(!device_is_ready(photo_transistor_enable_dt.port));
|
||||
RET_IF_ERR(gpio_pin_configure_dt(&photo_transistor_enable_dt, GPIO_OUTPUT));
|
||||
#elif DT_NODE_EXISTS(DT_NODELABEL(ldr))
|
||||
RET_IF_ERR(adc_channel_setup_dt(&adc_ldr_spec));
|
||||
RET_IF_ERR(!device_is_ready(ldr_enable_dt.port));
|
||||
RET_IF_ERR(gpio_pin_configure_dt(&ldr_enable_dt, GPIO_OUTPUT));
|
||||
#endif
|
||||
|
||||
for (size_t idx = 0; idx < ARRAY_SIZE(dry_coeffs); idx++) {
|
||||
LOG_DBG("Dry coeff %d: %d\n", idx, dry_coeffs[idx]);
|
||||
}
|
||||
for (size_t idx = 0; idx < ARRAY_SIZE(wet_coeffs); idx++) {
|
||||
LOG_DBG("Wet coeff %d: %d\n", idx, wet_coeffs[idx]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int prst_adc_soil_read(float battery_voltage, prst_adc_soil_moisture_t* out) {
|
||||
// Enable fast discharge circuit.
|
||||
RET_IF_ERR(gpio_pin_set_dt(&fast_disch_dt, 1));
|
||||
// Start PWM.
|
||||
RET_IF_ERR(pwm_set_dt(&soil_pwm_dt, soil_pwm_dt.period, pulse));
|
||||
k_msleep(30);
|
||||
RET_IF_ERR(read_adc_spec(&adc_soil_spec, &out->adc_read));
|
||||
// Stop PWM.
|
||||
RET_IF_ERR(pwm_set_dt(&soil_pwm_dt, 0, 0));
|
||||
// Turn off fast discharge circuit.
|
||||
RET_IF_ERR(gpio_pin_set_dt(&fast_disch_dt, 0));
|
||||
out->percentage =
|
||||
MAX(0.0f, MIN(1.0f, get_soil_moisture_percent(battery_voltage, buf)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prst_adc_photo_read(float battery_voltage, prst_adc_photo_sensor_t* out) {
|
||||
#if DT_NODE_EXISTS(DT_NODELABEL(photo_transistor))
|
||||
RET_IF_ERR(gpio_pin_set_dt(&photo_transistor_enable_dt, 1));
|
||||
k_msleep(10);
|
||||
RET_IF_ERR(read_adc_spec(&adc_photo_transistor_spec, &out->adc_read));
|
||||
RET_IF_ERR(gpio_pin_set_dt(&photo_transistor_enable_dt, 0));
|
||||
const float phototransistor_resistor = DT_PROP(DT_NODELABEL(photo_transistor), output_ohms);
|
||||
// Assuming 10000 lux for the saturation test. Calibration with a proper light
|
||||
// meter would be better.
|
||||
const float lux_sun = 10000.0f;
|
||||
const float current_sun = 3.59e-3f;
|
||||
const float current = out->adc_read.voltage / phototransistor_resistor;
|
||||
out->brightness = MAX(0, MIN(lux_sun * current / current_sun, UINT16_MAX));
|
||||
LOG_DBG("Read phototransistor: %u lx | %.2f V", out->brightness, out->adc_read.voltage);
|
||||
|
||||
#elif DT_NODE_EXISTS(DT_NODELABEL(ldr))
|
||||
RET_IF_ERR(gpio_pin_set_dt(&ldr_enable_dt, 1));
|
||||
k_msleep(10);
|
||||
RET_IF_ERR(read_adc_spec(&adc_ldr_spec, &out->adc_read));
|
||||
RET_IF_ERR(gpio_pin_set_dt(&ldr_enable_dt, 0));
|
||||
// The photo resistor forms a voltage divider with R.
|
||||
// The voltage here is measured in the middle of the voltage divider.
|
||||
// Vcc ---- (R_photo) ---|--- (R) ---- GND
|
||||
// Vout
|
||||
// So we can estimate R_photo = R * (Vcc - Vout) / Vout
|
||||
const float r = DT_PROP(DT_NODELABEL(ldr), output_ohms);
|
||||
const float photo_resistance =
|
||||
r * (battery_voltage - out->adc_read.voltage) / out->adc_read.voltage;
|
||||
// The relationship between the LDR resistance and the lux level is
|
||||
// logarithmic. We need to solve a logarithmic equation to find the lux
|
||||
// level, given the LDR resistance we just measured.
|
||||
// These values work for the GL5528 LDR and were borrowed from
|
||||
// https://github.com/QuentinCG/Arduino-Light-Dependent-Resistor-Library.
|
||||
const float mult_value = 32017200.0f;
|
||||
const float pow_value = 1.5832f;
|
||||
out->brightness =
|
||||
MAX(0, MIN(mult_value / powf(photo_resistance, pow_value), UINT16_MAX));
|
||||
LOG_DBG("Read LDR: %u lx | %.2f V", out->brightness, out->adc_read.voltage);
|
||||
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
68
code/nrf-connect/prstlib/src/button.c
Normal file
68
code/nrf-connect/prstlib/src/button.c
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#include "prstlib/button.h"
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "prstlib/led.h"
|
||||
#include "prstlib/macros.h"
|
||||
|
||||
LOG_MODULE_REGISTER(button, CONFIG_PRSTLIB_LOG_LEVEL);
|
||||
|
||||
static struct k_work_delayable button_pressed_delayable;
|
||||
|
||||
static struct gpio_dt_spec button =
|
||||
GPIO_DT_SPEC_GET(DT_NODELABEL(button0), gpios);
|
||||
|
||||
static struct gpio_callback cb_data;
|
||||
|
||||
static prst_button_callback_t user_callback = NULL;
|
||||
|
||||
static void maybe_call_user_callback(prst_button_t button, bool is_active) {
|
||||
if (user_callback != NULL) {
|
||||
user_callback(button, is_active);
|
||||
} else {
|
||||
LOG_WRN("No user callback registered for button %d", button);
|
||||
}
|
||||
}
|
||||
|
||||
static void button_pressed_cb(struct k_work *work) {
|
||||
int button_state = prst_button_poll(PRST_BUTTON_SW1);
|
||||
if (button_state < 0) {
|
||||
LOG_ERR("Failed to poll button");
|
||||
return;
|
||||
}
|
||||
return maybe_call_user_callback(PRST_BUTTON_SW1, button_state);
|
||||
}
|
||||
|
||||
static void button_pressed_isr(const struct device *dev, struct gpio_callback *cb,
|
||||
uint32_t pins) {
|
||||
k_work_reschedule(&button_pressed_delayable, K_MSEC(10));
|
||||
}
|
||||
|
||||
int prst_button_init() {
|
||||
RET_IF_ERR(!device_is_ready(button.port));
|
||||
RET_IF_ERR(gpio_pin_configure_dt(&button, GPIO_INPUT));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prst_button_register_callback(prst_button_callback_t callback) {
|
||||
k_work_init_delayable(&button_pressed_delayable, button_pressed_cb);
|
||||
// EDGE interrupts seem to consume more power than LEVEL ones.
|
||||
// For GPIO_INT_EDGE_BOTH: 16 uA idle.
|
||||
// For GPIO_INT_LEVEL_ACTIVE: 3 uA idle.
|
||||
// Related issue:
|
||||
// https://github.com/zephyrproject-rtos/zephyr/issues/28499
|
||||
// Apparently sense-edge-mask brings the power consumption down to
|
||||
// 3 uA for EDGE interrupts too.
|
||||
RET_IF_ERR(gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_BOTH));
|
||||
gpio_init_callback(&cb_data, button_pressed_isr, BIT(button.pin));
|
||||
RET_IF_ERR(gpio_add_callback(button.port, &cb_data));
|
||||
user_callback = callback;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prst_button_poll(prst_button_t prst_button) {
|
||||
RET_CHECK(prst_button == PRST_BUTTON_SW1, "Invalid button");
|
||||
return gpio_pin_get_dt(&button);
|
||||
}
|
||||
14
code/nrf-connect/prstlib/src/led.c
Normal file
14
code/nrf-connect/prstlib/src/led.c
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#include "prstlib/led.h"
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
#include "prstlib/macros.h"
|
||||
|
||||
LOG_MODULE_REGISTER(led, CONFIG_PRSTLIB_LOG_LEVEL);
|
||||
|
||||
struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_NODELABEL(led0), gpios);
|
||||
|
||||
int prst_led_init() {
|
||||
RET_IF_ERR(!device_is_ready(led.port));
|
||||
return gpio_pin_configure_dt(&led, GPIO_OUTPUT);
|
||||
}
|
||||
27
code/nrf-connect/prstlib/src/sensors.c
Normal file
27
code/nrf-connect/prstlib/src/sensors.c
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#include "prstlib/sensors.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "prstlib/adc.h"
|
||||
#include "prstlib/led.h"
|
||||
#include "prstlib/macros.h"
|
||||
|
||||
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.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 (%.2f%%)", sensors->batt.adc_read.millivolts,
|
||||
100 * sensors->batt.percentage);
|
||||
LOG_DBG("Soil: %.0f %%", 100 * sensors->soil.percentage);
|
||||
LOG_DBG("Photo: %u lx (%d mV)", sensors->photo.brightness,
|
||||
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("--------------------------------------------------");
|
||||
|
||||
return 0;
|
||||
}
|
||||
52
code/nrf-connect/prstlib/src/shtc3.c
Normal file
52
code/nrf-connect/prstlib/src/shtc3.c
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include "prstlib/shtc3.h"
|
||||
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "prstlib/macros.h"
|
||||
|
||||
LOG_MODULE_REGISTER(shtc3, CONFIG_PRSTLIB_LOG_LEVEL);
|
||||
|
||||
static const struct i2c_dt_spec shtc3 = I2C_DT_SPEC_GET(DT_NODELABEL(shtc3));
|
||||
|
||||
static uint8_t buff[6];
|
||||
|
||||
static int write_cmd(uint16_t command) {
|
||||
static uint8_t cmd[2];
|
||||
cmd[0] = command >> 8;
|
||||
cmd[1] = command & 0xff;
|
||||
RET_IF_ERR(i2c_write_dt(&shtc3, cmd, sizeof(cmd)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prst_shtc3_read(prst_shtc3_read_t *out) {
|
||||
RET_IF_ERR_MSG(!device_is_ready(shtc3.bus), "SHTC3 is not ready");
|
||||
|
||||
// Wake the sensor up.
|
||||
RET_IF_ERR(write_cmd(PRST_SHTC3_CMD_WAKEUP));
|
||||
k_msleep(1);
|
||||
|
||||
// Request measurement.
|
||||
RET_IF_ERR(write_cmd(PRST_SHTC3_CMD_MEASURE_TFIRST_NORMAL));
|
||||
|
||||
// Reading in normal (not low power) mode can take up to 12.1 ms, according to
|
||||
// the datasheet.
|
||||
k_msleep(20);
|
||||
|
||||
// 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));
|
||||
|
||||
// TODO: verify the CRC of the measurements. The function is described in the
|
||||
// datasheet.
|
||||
|
||||
out->temp_c = -45 + 175 * ((float)((buff[0] << 8) | buff[1])) / (1 << 16);
|
||||
out->rel_humi = ((float)((buff[3] << 8) | buff[4])) / UINT16_MAX;
|
||||
|
||||
LOG_DBG("Read temp: %f oC (%d)", out->temp_c, (int)out->temp_c);
|
||||
LOG_DBG("Read humi: %.0f %%", 100.0 * out->rel_humi);
|
||||
return 0;
|
||||
}
|
||||
2
code/nrf-connect/samples/ble/.gitignore
vendored
Normal file
2
code/nrf-connect/samples/ble/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
build
|
||||
build_*
|
||||
21
code/nrf-connect/samples/ble/CMakeLists.txt
Normal file
21
code/nrf-connect/samples/ble/CMakeLists.txt
Normal file
|
|
@ -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(ble)
|
||||
|
||||
include_directories(src)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/ble.c
|
||||
src/encoding.c
|
||||
)
|
||||
|
||||
add_subdirectory(../../prstlib prstlib)
|
||||
target_include_directories(app PRIVATE ../../prstlib/include)
|
||||
target_link_libraries(app PUBLIC prstlib)
|
||||
52
code/nrf-connect/samples/ble/Kconfig
Normal file
52
code/nrf-connect/samples/ble/Kconfig
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
source "Kconfig.zephyr"
|
||||
rsource "../../prstlib/Kconfig"
|
||||
|
||||
config PRST_SLEEP_DURATION_MSEC
|
||||
int "Sleep duration in milliseconds"
|
||||
default 600000
|
||||
|
||||
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"
|
||||
default 30
|
||||
|
||||
config PRST_BLE_MAX_ADV_INTERVAL
|
||||
int "Maximum advertising interval in milliseconds"
|
||||
default 40
|
||||
|
||||
choice PRST_BLE_ENCODING
|
||||
prompt "b-parasite BLE encoding"
|
||||
default PRST_BLE_ENCODING_BTHOME_V2
|
||||
|
||||
config PRST_BLE_ENCODING_BPARASITE_V2
|
||||
bool "Uses the custom b-parasite protocol v2 for encoding advertising packets"
|
||||
|
||||
config PRST_BLE_ENCODING_BTHOME_V1
|
||||
bool "Uses the BTHome (bthome.io) BLE encoding (v1)"
|
||||
|
||||
config PRST_BLE_ENCODING_BTHOME_V2
|
||||
bool "Uses the BTHome (bthome.io) BLE encoding (v2)"
|
||||
|
||||
endchoice # PRST_BLE_ENCODING
|
||||
|
||||
config PRST_BLE_ENCODING_SERVICE_DATA_LEN
|
||||
int
|
||||
help
|
||||
Size of the service data buffer.
|
||||
default 20 if PRST_BLE_ENCODING_BPARASITE_V2
|
||||
default 18 if PRST_BLE_ENCODING_BTHOME_V1
|
||||
default 19 if PRST_BLE_ENCODING_BTHOME_V2
|
||||
|
||||
|
||||
config PRST_BLE_HAS_USER_DEFINED_RANDOM_STATIC_ADDR
|
||||
bool "Whether to use a custom BLE address"
|
||||
|
||||
config PRST_BLE_USER_DEFINED_RANDOM_STATIC_ADDR
|
||||
string "Overrides the device's BT address"
|
||||
help
|
||||
This address has to be random static (e.g.: f0:ca:f0:ca:01:d5).
|
||||
depends on PRST_BLE_HAS_USER_DEFINED_RANDOM_STATIC_ADDR
|
||||
55
code/nrf-connect/samples/ble/README.md
Normal file
55
code/nrf-connect/samples/ble/README.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# Bluetooth Low Energy (BLE)
|
||||
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.
|
||||
|
||||
### Sleep Interval
|
||||
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_MSEC` before going back to sleep.
|
||||
|
||||
### Advertising Packet Encoding
|
||||
There are different ways to encode the sensor data in a BLE advertising packet.
|
||||
|
||||
#### BTHome Encoding
|
||||
[BTHome](https://bthome.io) is a new (as of Dec/2022) open standard for encoding sensor data in BLE applications. [Home Assistant](https://www.home-assistant.io/integrations/bthome/) supports it out of the box. This makes the deployment extra convenient, since no additional configuration is needed - Home Assistant will automatically detect b-parasites in range.
|
||||
|
||||
What's even more interesting is that by employing [ESPHome](https://esphome.io/) bridges with the [`bluetooth_proxy`](https://esphome.io/components/bluetooth_proxy.html) component, the range of BLE coverage can be transparently increased. Multiple ESPHome bridges will forward received BLE broadcasts to Home Assistant.
|
||||
|
||||
This is what a typical deployment with BTHome looks like:
|
||||

|
||||
|
||||
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
|
||||
#### b-parasite Encoding
|
||||
`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:
|
||||
|
||||

|
||||
|
||||
The disadvantages of this encoding are:
|
||||
- Each b-parasite has to be configured in the ESPHome component
|
||||
- Range is limited, unless multiple ESPHome bridges are deployed with the same static configuration
|
||||
|
||||
## Battery Life
|
||||
**tl;dr**: Theoretically well over two years with default settings.
|
||||
|
||||
While in deep sleep, the board consumes around `3.0 uA`:
|
||||
|
||||

|
||||
|
||||
In the active broadcasting state, the average power consumption is highly dependant on the advertising interval.
|
||||
|
||||
With the default settings (in the `[30, 40] ms` range), we see an average of around `810 uA`:
|
||||
|
||||

|
||||
|
||||
If for example we lower the connection interval to the SDK defaults (`[100, 150] ms`, roughly trading off range for power), the average current consumption is around `345 uA`:
|
||||
|
||||

|
||||
|
||||
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.
|
||||
16
code/nrf-connect/samples/ble/ble.code-workspace
Normal file
16
code/nrf-connect/samples/ble/ble.code-workspace
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"path": "../../prstlib"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"C_Cpp.autoAddFileAssociations": false,
|
||||
"nrf-connect.applications": [
|
||||
"${workspaceFolder}"
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
After Width: | Height: | Size: 217 KiB |
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
After Width: | Height: | Size: 238 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 302 KiB |
BIN
code/nrf-connect/samples/ble/media/power-profile/sleep.png
Normal file
BIN
code/nrf-connect/samples/ble/media/power-profile/sleep.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
86
code/nrf-connect/samples/ble/nrf52840dk_nrf52840.overlay
Normal file
86
code/nrf-connect/samples/ble/nrf52840dk_nrf52840.overlay
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
&pinctrl {
|
||||
/* Configure pwm0 instance to use pin 5. */
|
||||
pwm0_default: pwm0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(PWM_OUT0, 0, 5)>;
|
||||
nordic,invert;
|
||||
};
|
||||
};
|
||||
|
||||
pwm0_sleep: pwm0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(PWM_OUT0, 0, 5)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
|
||||
/* Configure i2c0 instance to use pins 24 (SDA) & 13 (SCL). */
|
||||
i2c0_default: i2c0_default {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(TWIM_SDA, 0, 24)>,
|
||||
<NRF_PSEL(TWIM_SCL, 0, 13)>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c0_sleep: i2c0_sleep {
|
||||
group1 {
|
||||
psels = <NRF_PSEL(TWIM_SDA, 0, 24)>,
|
||||
<NRF_PSEL(TWIM_SCL, 0, 13)>;
|
||||
low-power-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
shtc3: shtc3@70 {
|
||||
compatible = "i2c-device";
|
||||
reg = <0x70>;
|
||||
label = "SHTC3";
|
||||
};
|
||||
};
|
||||
|
||||
&adc {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
// P0.03.
|
||||
zephyr,input-positive = <NRF_SAADC_AIN1>;
|
||||
zephyr,resolution = <10>;
|
||||
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
// P0.02.
|
||||
zephyr,input-positive = <NRF_SAADC_AIN0>;
|
||||
zephyr,resolution = <10>;
|
||||
};
|
||||
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
zephyr,gain = "ADC_GAIN_1_6";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
zephyr,input-positive = <NRF_SAADC_VDD>;
|
||||
zephyr,resolution = <10>;
|
||||
};
|
||||
};
|
||||
|
||||
/ {
|
||||
zephyr,user {
|
||||
io-channels = <&adc 0>, <&adc 1>, <&adc 2>;
|
||||
};
|
||||
|
||||
soil_pwm: soil_pwm {
|
||||
compatible = "pwm-fixed";
|
||||
pwms = <&pwm0 0 PWM_MSEC(100) PWM_POLARITY_INVERTED>;
|
||||
pulse = <PWM_MSEC(50)>;
|
||||
};
|
||||
};
|
||||
27
code/nrf-connect/samples/ble/prj.conf
Normal file
27
code/nrf-connect/samples/ble/prj.conf
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
CONFIG_LOG=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_CBPRINTF_FP_SUPPORT=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_ADC=y
|
||||
CONFIG_GPIO=y
|
||||
|
||||
CONFIG_BT=y
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_DEVICE_NAME="prst"
|
||||
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
|
||||
|
||||
CONFIG_SERIAL=n
|
||||
|
||||
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_MSEC=1000
|
||||
|
||||
# prstlib config - ser all options in prstlib/Kconfig.
|
||||
# CONFIG_PRSTLIB_LOG_LEVEL_DBG=y
|
||||
80
code/nrf-connect/samples/ble/src/ble.c
Normal file
80
code/nrf-connect/samples/ble/src/ble.c
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#include "ble.h"
|
||||
|
||||
#include <prstlib/macros.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
|
||||
#include "encoding.h"
|
||||
|
||||
LOG_MODULE_REGISTER(ble, LOG_LEVEL_INF);
|
||||
|
||||
static uint8_t service_data[CONFIG_PRST_BLE_ENCODING_SERVICE_DATA_LEN] = {0};
|
||||
|
||||
static const struct bt_data ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
BT_DATA(BT_DATA_SVC_DATA16, service_data, ARRAY_SIZE(service_data)),
|
||||
BT_DATA_BYTES(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME),
|
||||
};
|
||||
|
||||
static bt_addr_le_t mac_addr;
|
||||
|
||||
static int set_user_defined_bt_addr(const char *addr_str) {
|
||||
bt_addr_le_t addr;
|
||||
RET_IF_ERR(bt_addr_le_from_str(addr_str, "random", &addr));
|
||||
RET_IF_ERR(bt_id_create(&addr, NULL));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// bt_addr_le_t.a holds the MAC address in big-endian.
|
||||
static int get_mac_addr(bt_addr_le_t *out) {
|
||||
struct bt_le_oob oob;
|
||||
RET_IF_ERR(bt_le_oob_get_local(BT_ID_DEFAULT, &oob));
|
||||
const uint8_t *addr = oob.addr.a.val;
|
||||
LOG_INF("MAC Address: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
|
||||
*out = oob.addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prst_ble_init() {
|
||||
#if CONFIG_PRST_BLE_HAS_USER_DEFINED_RANDOM_STATIC_ADDR
|
||||
RET_IF_ERR(set_user_defined_bt_addr(CONFIG_PRST_BLE_USER_DEFINED_RANDOM_STATIC_ADDR));
|
||||
#else
|
||||
UNUSED_OK(set_user_defined_bt_addr);
|
||||
#endif // PRST_BLE_HAS_USER_DEFINED_MAC_ADDR
|
||||
|
||||
RET_IF_ERR(bt_enable(/*bt_reader_cb_t=*/NULL));
|
||||
if (IS_ENABLED(CONFIG_SETTINGS)) {
|
||||
RET_IF_ERR_MSG(settings_load(), "Error in settings_load()");
|
||||
}
|
||||
|
||||
RET_IF_ERR(get_mac_addr(&mac_addr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PRST_MS_TO_INTERVAL(value_ms) ((uint16_t)(value_ms) / 0.625f)
|
||||
|
||||
int prst_ble_adv_start() {
|
||||
return bt_le_adv_start(
|
||||
BT_LE_ADV_PARAM(
|
||||
BT_LE_ADV_OPT_USE_IDENTITY,
|
||||
PRST_MS_TO_INTERVAL(CONFIG_PRST_BLE_MIN_ADV_INTERVAL),
|
||||
PRST_MS_TO_INTERVAL(CONFIG_PRST_BLE_MAX_ADV_INTERVAL),
|
||||
/*_peer=*/NULL),
|
||||
ad,
|
||||
ARRAY_SIZE(ad),
|
||||
// sd == NULL is required to set advertising to non-connectable. See
|
||||
// https://github.com/zephyrproject-rtos/zephyr/blob/c0fcd35531611bbe35376c62a9e50744d6904940/subsys/bluetooth/host/adv.c#L860
|
||||
/*sd=*/NULL,
|
||||
/*sd_len=*/0);
|
||||
}
|
||||
|
||||
int prst_ble_adv_stop() {
|
||||
return bt_le_adv_stop();
|
||||
}
|
||||
|
||||
int prst_ble_adv_set_data(const prst_sensors_t *sensors) {
|
||||
return prst_ble_encode_service_data(sensors, &mac_addr, service_data,
|
||||
sizeof(service_data));
|
||||
}
|
||||
11
code/nrf-connect/samples/ble/src/ble.h
Normal file
11
code/nrf-connect/samples/ble/src/ble.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef _PRST_BLE_BLE_H_
|
||||
#define _PRST_BLE_BLE_H_
|
||||
|
||||
#include <prstlib/sensors.h>
|
||||
|
||||
int prst_ble_init();
|
||||
int prst_ble_adv_start();
|
||||
int prst_ble_adv_stop();
|
||||
int prst_ble_adv_set_data(const prst_sensors_t *sensors);
|
||||
|
||||
#endif // _PRST_BLE_BLE_H_
|
||||
139
code/nrf-connect/samples/ble/src/encoding.c
Normal file
139
code/nrf-connect/samples/ble/src/encoding.c
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#include "encoding.h"
|
||||
|
||||
#include <prstlib/macros.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(ble, LOG_LEVEL_INF);
|
||||
|
||||
int prst_ble_encode_service_data(const prst_sensors_t* sensors,
|
||||
const bt_addr_le_t* bt_addr, uint8_t* out,
|
||||
uint8_t out_len) {
|
||||
RET_CHECK(out_len >= CONFIG_PRST_BLE_ENCODING_SERVICE_DATA_LEN,
|
||||
"Buffer is not large enough");
|
||||
|
||||
#if CONFIG_PRST_BLE_ENCODING_BPARASITE_V2
|
||||
// 0x181a - Environmental sensing service UUID.
|
||||
out[0] = 0x1a;
|
||||
out[1] = 0x18;
|
||||
// Four bits for the protocol version.
|
||||
out[2] |= (2 << 4) & 0xf0;
|
||||
// Bit 0 of byte 0 specifies whether or not ambient light data exists in the
|
||||
// payload.
|
||||
#if DT_NODE_EXISTS(DT_NODELABEL(photo_transistor)) || DT_NODE_EXISTS(DT_NODELABEL(ldr))
|
||||
out[2] |= 1;
|
||||
#endif
|
||||
// 4 bits for a small wrap-around counter for deduplicating messages on the
|
||||
// receiver.
|
||||
|
||||
static uint8_t run_counter;
|
||||
|
||||
out[3] = run_counter++ & 0x0f;
|
||||
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;
|
||||
uint16_t humi = sensors->shtc3.rel_humi * UINT16_MAX;
|
||||
out[8] = humi >> 8;
|
||||
out[9] = humi & 0xff;
|
||||
uint16_t soil_moisture = sensors->soil.percentage * UINT16_MAX;
|
||||
out[10] = soil_moisture >> 8;
|
||||
out[11] = soil_moisture & 0xff;
|
||||
// MAC address in big-endian.
|
||||
memcpy(out + 12, bt_addr->a.val, BT_ADDR_SIZE);
|
||||
out[18] = sensors->photo.brightness >> 8;
|
||||
out[19] = sensors->photo.brightness & 0xff;
|
||||
|
||||
// https://bthome.io/v1/
|
||||
#elif CONFIG_PRST_BLE_ENCODING_BTHOME_V1
|
||||
|
||||
// 0x181c - User data service UUID.
|
||||
out[0] = 0x1c;
|
||||
out[1] = 0x18;
|
||||
|
||||
// 1. Soil moisture.
|
||||
// uint16_t.
|
||||
out[2] = (0b000 << 5) | 3;
|
||||
// Type of measurement - Moisture.
|
||||
out[3] = 0x14;
|
||||
// Value. 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[4] = soil_val & 0xff;
|
||||
out[5] = soil_val >> 8;
|
||||
// 2. Temp.
|
||||
// int16_t.
|
||||
out[6] = (0b001 << 5) | 3;
|
||||
// Type of measurement - temperature.
|
||||
out[7] = 0x02;
|
||||
// Value. Factor 0.01.
|
||||
int16_t temp_val = 100 * sensors->shtc3.temp_c;
|
||||
out[8] = temp_val & 0xff;
|
||||
out[9] = temp_val >> 8;
|
||||
// 3. Humidity
|
||||
// uint16_t.
|
||||
out[10] = (0b000 << 5) | 3;
|
||||
// Type - humidity.
|
||||
out[11] = 0x03;
|
||||
// Value. Factor 0.01, over 100%.
|
||||
uint16_t humi_val = 10000 * sensors->shtc3.rel_humi;
|
||||
out[12] = humi_val & 0xff;
|
||||
out[13] = humi_val >> 8;
|
||||
// 4. Battery voltage.
|
||||
// uint16_t.
|
||||
out[14] = (0b000 << 5) | 3;
|
||||
// Type - voltage.
|
||||
out[15] = 0x0c;
|
||||
// Value. Factor of 0.001.
|
||||
uint16_t batt_val = sensors->batt.adc_read.millivolts;
|
||||
out[16] = batt_val & 0xff;
|
||||
out[17] = batt_val >> 8;
|
||||
|
||||
// https://bthome.io/format/
|
||||
#elif CONFIG_PRST_BLE_ENCODING_BTHOME_V2
|
||||
// 0xfcd2 - bthome.io service UUID.
|
||||
out[0] = 0xd2;
|
||||
out[1] = 0xfc;
|
||||
|
||||
// Service header - no encryption, bt home v2.
|
||||
out[2] = 0x40;
|
||||
|
||||
// Temperature.
|
||||
out[3] = 0x02;
|
||||
int16_t temp_val = 100 * sensors->shtc3.temp_c;
|
||||
out[4] = temp_val & 0xff;
|
||||
out[5] = temp_val >> 8;
|
||||
// Humidity.
|
||||
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[8] = 0x05;
|
||||
// Value. Factor of 0.01.
|
||||
uint32_t lux_val = sensors->photo.brightness * 100;
|
||||
out[9] = lux_val & 0xff;
|
||||
out[10] = (lux_val >> 8) & 0xff;
|
||||
out[11] = (lux_val >> 16) & 0xff;
|
||||
// Battery voltage.
|
||||
out[12] = 0x0c;
|
||||
// Value. Factor of 0.001.
|
||||
uint16_t batt_val = sensors->batt.adc_read.millivolts;
|
||||
out[13] = batt_val & 0xff;
|
||||
out[14] = batt_val >> 8;
|
||||
// Soil moisture.
|
||||
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;
|
||||
}
|
||||
11
code/nrf-connect/samples/ble/src/encoding.h
Normal file
11
code/nrf-connect/samples/ble/src/encoding.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef _PRST_BLE_ENCODING_H_
|
||||
#define _PRST_BLE_ENCODING_H_
|
||||
|
||||
#include <prstlib/sensors.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
|
||||
int prst_ble_encode_service_data(const prst_sensors_t* sensors,
|
||||
const bt_addr_le_t* bt_addr, uint8_t* out,
|
||||
uint8_t out_len);
|
||||
|
||||
#endif // _PRST_BLE_ENCODING_H_
|
||||
42
code/nrf-connect/samples/ble/src/main.c
Normal file
42
code/nrf-connect/samples/ble/src/main.c
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#include <prstlib/adc.h>
|
||||
#include <prstlib/button.h>
|
||||
#include <prstlib/led.h>
|
||||
#include <prstlib/macros.h>
|
||||
#include <prstlib/sensors.h>
|
||||
#include <prstlib/shtc3.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/pm/pm.h>
|
||||
#include <zephyr/pm/policy.h>
|
||||
|
||||
#include "ble.h"
|
||||
|
||||
LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
__ASSERT(!prst_loop(&sensors), "Error in prst_loop()");
|
||||
k_msleep(CONFIG_PRST_SLEEP_DURATION_MSEC);
|
||||
}
|
||||
}
|
||||
2
code/nrf-connect/samples/blinky/.gitignore
vendored
Normal file
2
code/nrf-connect/samples/blinky/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
build
|
||||
build_*
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue