Home
| pfodApps/pfodDevices
| WebStringTemplates
| Java/J2EE
| Unix
| Torches
| Superannuation
| CRPS Treatment
|
| About
Us
|
Easy Very Low Power BLE in Arduino -- Part 1
|
by Matthew Ford 13th Nov 2024 (originally posted 14th
December 2018)
©
Forward Computing and Control Pty. Ltd. NSW Australia
All rights
reserved.
Update: 11th Nov 2024 – Rev
13 of pfod_lp_nrf52_2024.zip fixed A0 to A3 pin mapping for
nRF52832_BARE_MODULE and added note about comparator pins and
programming wiring
Update: 23rd March 2024 – Pi Pico
prorgrammer no longer recommended. Use MuseLab MuseLab DAPLink
instead.
Update: 10th March 2024 – pfod_lp_nrf52_2024.zip
simplifies programming and handles protection on latest chip
version
Update: 31st May 2023 – Rev 11 of
pfod_lp_nrf52_2023.zip adds lp_comparator_isRunning() and changes
lp_timer to finer ticks, 30.5us. Max 511sec, min 5 ticks
=>152.5us
Update: 10th May
2023 – Skylab SKB369 back in stock at <US$5 on
Aliexpress
Update: 10th April
2023 – Added Raspberry Pi Pico DIY programmer
Update: 6th
February 2023 – Rev 10 of pfod_lp_nrf25_2022 adds
lp_ADC_calibrate() method to calibrate the ADC offset
Update: 6th
January 2023 – Rev 9 of pfod_lp_nrf25_2022 adds scanning
support methods to BLEPeripheral and lp_ADC, revised Muselab
programmer, added Help My Upload Failed
Update: 20th
February 2022 – added MuseLab programmer
Update: 12th
February 2022 – pfod_lp_nrf52_2022 Rev6 mods for 1/4
supply current (<20uA) and 4 times tx speed, corrected notes on
Particle Debugger availability
Update: 4th
February 2022 – Completely revised to use currently
available devices and programmer
For reliable programming it is best to solder the leads from the programmer to the board. While this tutorial shows jumper pins, it has been found that those connections become intermittent after repeated connects/disconnects which results in programming error messages. Soldering directly to the board overcomes those problems.
This tutorial, Building Very Low Power BLE devices made Easy with Arduino – 2022/2024, is Part 1 of 3.
Part 1 – Building Very Low Power BLE devices made Easy with Arduino, this one, covers building the CMSIS-DAP programmer and setting up Arduino to code nRF52 low power devices, the programming module and measuring the supply current. It also covers specialised low power timers and comparators and debounced inputs and using pfodApp to connect to and control the nRF52 device.
Part 2 – A Very Simple, Very Low Power Temperature Monitor covers using just a minimal nRF52 module and a coin cell to build a temperature monitor that runs for 5 years.
Part 3 – An Indoor / Outdoor Weather Station covers extending the very low power temperature sensor to add a sensor for temperature, relative humidity and barometric pressure and display the values on a Weather Station. The sensors run for ~4 ½ years
This tutorial is designed to allow the novice user to build very low power BLE devices, <20uA continuously while waiting for a connection and ~65uA while connected and sending/receiving data. All that is needed is familiarity with the Arduino IDE, some soldering proficiency and a multimeter. No Android coding is required.
This detailed tutorial uses Nordic Semiconductor nRF52832 modules and has been revised to handle the chip protection of the latest versions of the nRF52832 modules. This tutorial shows you how to code them using the Arduino IDE. It also covers how to connect to your BLE device from your Android phone and how to design custom menus and graphical displays.
Custom libraries are provided, based on Nordic's SDK and BLE support and Sandeepmistry's nRF5 IDE add-on and BLEPeripherial libraries. These libraries have been modified to support low power and to simplify use.
On the Android side, the tutorial uses a number of free Nordic Android apps, for testing and basic control. For custom Android displays, no Android coding is required. The free pfodDesigner Android app generates the low power Arduino code to display your own custom menus, charts, data logging etc on the (paid) pfodApp. You can also build custom interactive graphical controls in Arduino code for pfodApp. No Android coding required, pfodApp handles all of that for you.
Update 4th February
2022
This
tutorial completely replaces the previous Very
Low Power BLE made Easy with Arduino – 2019
tutorial.
Most of the programmers and BLE modules used in that 2019 tutorial
are no longer available or exorbitantly expensive.
This project was originally posted in December 2018 and used a
BlackMagic programming module and a Redbear Programmer for
programming the Redbear Nano V2 board. Later when the Redbear
Programmer was no longer available, it was replaced with a Particle
Debugger. Redbear Nano V2 is discontinued and the Particle Debugger
and the BlackMagic programmer are out-of-stock. The Particle Debugger
appears to still be a current item, just not available as at Feb
2022. Also the original project used a SkyLab Bluetooth Module SKB369
or a GT832E_01 as an alternative to the Redbear Nano V2. The
SKB369 is still available but at a quoted price of US$190 each as at
January 2022. Update: 10th May 2023 – Skylab SKB369
back in stock at <US$5 on Aliexpress.
This version
pfod_lp_nrf52_2024 only supports programming via CMSIS-DAP
programmers
The GT832E_01 (nRF52832 version), is used here as an
example. It is still available at ~US$15 each, as at Jan 2022, but
that price is also higher than previously.
The BLM_KTB522
available from via Aliexpress for ~US$6 each does not have an
external low frequency 32Khz RTC crystal so should be programmed
using the Generic nRF52832 (LFC RC osc) board
setting.
The XL52832-D01 from Aliexpress for ~US$5
has lots of I/O pins (Jessinne on Aliexpress has Test Board Adaptor
Plate, (breakout board) for this module). Search for nRF52832 on
Aliexpress for other boards. Make sure the board is an nRF52832
not the cheaper nRF51822.
The Holyiot JY-16013-NRF52832
(~US$4.30 from Aliexpress) that has 6-8 I/O pins (+VDD/GND) on a 0.5”
spacing that are accessible from the top and edge of the board.
Holyiot also has nRF52832 'bare' modules in other board/pin
configurations.
Adafruit also has an in-expensive
nrf52832 bare module, ~US$10, also with lots of I/O pins on a
fine spacing 0.7mm so a PCB is need for this module.
Other
alternative nRF52832 modules will be covered as samples become
available.
Wire up the programmer, install the low power support and use the free pfodDesigner to create a custom control menu/data logger and generate the low power sketch for pfodApp to connect to and display the controls and chart and log the data.
Boards
and Programmers – MuseLab and Raspberry Pi Pico
versions
Building the CMSIS-DAP programming
module
Building the programming/test
board
Installing the low power support
for the nRF52832 in Arduino
Pin Mappings
for Generic nRF52832 boards
Programming
the GT832E_01 module
Help My Upload
Failed
Issue with
NRF52 unexpectedly entering debug mode
How
to Code for Low Power
Measuring the
Supply Current – Blink_millisDelay.ino
A
Low Power Timer – Blink_lp_timer_GT832E_01.ino
nRF52
Low Power Optimizations
Debugging Low
Power
A Low Power BLE UART –
lp_BLE_temp_GT832E_01.ino
Even Lower Power
BLE, <20uA – LowerPower_Blink_GT832E_01.ino
Sending
data via lp_BLESerial –
lp_BLE_temp_GT832_01.ino
lp_comparator
– lp_BLE_comparator_GT832E_01.ino
lp_pinChange
lp_ADC
High Drive Output Modes
nrf52ChipInfo
Low
Power Button Debounce –
lp_BLE_debounce_GT832E_01.ino
Custom Low Power
Control and Data LoggingRemoving
nRF52 program protection –
Not necessary with pfod_lp_nrf52_2024
Although Sandeepmistry's original nRF5 Arduino add-on supports a number of nRF51/52 boards, this project only supports nRF52832 chips programmed via CMSIS-DAP programmers. To get the lowest power you need to use a board with just the nRF52832 chip since any extra devices onboard like accelerometers/leds will use more power. The pfod_lp_nrf52_2024.zip covers all nRF52832 bare modules, as well as the historical SKB369 and Redbear Nano V2 and other nRF53823 devices.
The Chinese company, MuseLab, produces inexpensive CMSIS-DAP (~US$5), DAPLink(~US%6) and CMSIS-DAP-Wifi modules. The DAPLink version is used here for programming on Windows 10 as it is more reliable. They are available from Aliexpress and also there appear to be a lot of copies available.
However also see below for a very simple DIY
Raspberry Pi Pico CMSIS-DAP-V2 programmer. No
Longer Recommended.
For Windows 7, mbedWinSerial_16466.exe (local copy here) recognises the DAP-LINK and CMSIS-DAP devices and installs the COM driver.
The DAPLink version adds a drag and drop DAPLink drive. That DAPLink drive is not used here. On Windows 10 it installs a drive letter that you can drag and drop to. On Windows 7, it needs this CMSIS_DAP_v2.inf file to install the unsigned driver for the DAPLink drive.
The third MuseLab option is the more expensive CMSIS-DAP-Wifi programmer, not recommeded, which connects via local wifi between the module connected to the computer and the target programmer. This CMSIS-DAP-Wifi works for programming but is not recommended because it buffers the debug serial output before sending it back to the computer and misses some of the target serial output data while it sends the buffered data. Tested with the Blink_lp_timer_debug_GT832E_01.ino from the Debugging Low Power section below. Also this module is not recognized by mbedWinSerial_16466.exe and so you have to use process described here to install the COM driver on Windows 7 (and other versions?)
You can very easily build your own CMSIS-DAP-V2 programmer
from a Raspberry Pi Pico board and four 100 ohm resistors as
described here.
Has been tested for removing program protection, programming via
Arduino and Serial output.
You can also build your own CMSIS-DAP programmer from the ubiquitious STM32F103C8 (“BluePill”) as described here. This was the programmer originally used for this tutorial but is more complicated to build than the Raspberry Pi Pico based version above.
There are many other CMSIS-DAP programmers available, such as this CMSIS-DAP Simulator for ~US$2.15 ,but note is has different pin outs. It has been tested for programming. Not yet tested for Serial.
As of Feb 2022, the Particle Debugger was out of stock, but still appears to be a current item. If you want to use the Particle Debugger to program your nrf52832, then refer to Easy Very Low Power BLE in Arduino (2019) -- Part 1 for the programmer construction details. but then continue here for installing the latest pfod_lp_nrf52_2024 Rev 11 board support.
For
programming there is also an ESP-01/ESP8266 based BlackMagic WiFi
clone from https://github.com/walmis/blackmagic-esp8266
with pre-compiled binaries at
https://github.com/J-Wrobel/blackmagic-espidf/tree/master/bins. The
no_OTA version is all you need.
This
programmer has a number of restrictions
1) openOCD does not
connect for the removal of chip programming protection.
https://github.com/walmis/openocd-blackmagic
has a version of openOCD that includes a blackmagic interface (not
tested)
2) It requires changes to the Arduino upload scripts, in
platform.txt, to access an IP:port instead of the serial COM port.
3) The IP network is fix as 192.168.4.1 (unless you edit and
recompile the source) and you need to disconnect your computer from
the internet to connect to this local network for programming. You
may also need to set a static IP for your computer if you normal
local network is not 192.168.x.x.
The DAPLink programmer is the most reliable (See Help My Upload Failed below). It is less sensitive to the size of the protection resistors in the SWCLK and SWDIO lines.
This minimal programmer has 100 ohm resistors in the SWCLK, SWDIO, RX and TX lines to protect against plugging in to the wrong pins. There is no protection on the 3v3 and Gnd lines. You just have to be careful and double check. Remember “one flash and its ash”
The Raspberry Pi Pico programmer is similar with 100 ohm protection resistors added.
Approximate cost as at Feb 2022, ~US$24 including USB extension cable (excluding nylon screws/nuts and shipping and an nRF52832 module)
MuseLab
DAPLink module (click on the DAPLink option) – ~US$6 from
Aliexpress
USB extension cable – ~US$2 Sparkfun
CAB-13309
OR
Raspberry Pi Pico – ~US$4
Adafruit
4864 ~US$4 or your favourite supplier
3 x 0.1uF 25V Ceramic capacitor e.g. Mouser
FX18X7R1E104K ~ US$1.5
3 x 22uF 16V Ceramic capacitor e.g.
Mouser
FK20X7R1C226M ~ US$1.8
1 x 300R 1/4W 1% resistor e.g. Mouser
MF0204FTE52-300R ~US$0.1
1 x 2K2 1/4W 1% resistor e.g. Mouser
MFR-12FTE52-2K2 ~US$0.1
4 x 100 ohm 1/4W 1% resistor e.g.
Mouser
MFR-25FTE52-100R ~US$0.4
2 x 4 pin and 1 x 2pin female headers
sockets e.g. Sparkfun
PRT-11269 ~US$2 cut down the 8pin header
6 x 6 pin male header
pins e.g. Sparkfun
PRT-00116 ~US$1.5
female to female jumper e.g. Adafruit
ID: 1951 ~US$2
male to male jumper e.g. Adafruit
ID: 1956 ~US$2
Vero board (strip copper) e.g. Jaycar
HP9540 ~AUD$5.5
plastic/cardboard sheet to insulate bottom of
vero board e.g. cut out of plastic lid
and tape or nylon screws to
hold it in place eg. 3mm x 12mm nylon screws, e.g. Jaycar
HP0140 ~AUD$3 and 3mm x 12mm nylon tapped spacers, e.g. Jaycar
HP0924 ~AUD$10
GT832E_01
nRF52832 BLE module ~ US$15 from Aliexpress
OR
XL52832-D01 nRF52832 BLE module ~ US$5.5 from Aliexpress
PLUS Jessinie Test Board Adaptor for nRF52832 ~ US$1.70
In this circuit, the 3 x 22uF are used to supply the chip's Tx current pulses. Otherwise the voltage drop across the current monitor resistors will cause the nRF52832 to shut down. The oscilloscope monitoring the current is connected between Target GND and Supply GND.
A small header board is used to connect the SWCLK and SWDIO leads and at the same time short out the Current Monitor Resistors so that programming, which draws more current, does not fail on low voltage. There is also another header for the Tx and Rx for the Arduino Serial Debugging connection. The SWCLK, SWDIO, Tx and Rx leads all have 100R resistors in series to protect against miss wiring to the nRF52832 pins. The 100R resistors limit the maximum current that flow via the in-chip I/O protection diodes to 33mA.
Regardless whether you use the 3V3 lead or another external supply to power the nRF52832, the negative (GND) return from the nRF52832 should be connected to the Target GND lead so that the return current will flow through the current monitoring resistors. If using and external supply, connect its -ve lead NOT to the nRF52832, but to the Supply GND lead.
This project builds on the Sandeepmistry's Arduino Core for Nordic Semiconductor nRF5 based boards and his BLEPeripheral library, but has been modified to add low-power support and an updated OpenOCD version and to only work with Nordic's nRF52832 via CMSIS-DAP. The modified library provides a number of low power utilities, like sleep, lp_timer and lp_comparator. It also provides a general purpose low power Nordic UART BLE service, lp_BLESerial, which works with Nordic's free apps and with pfodApp. The free pfodDesigner app can be used to generate a low power Arduino sketch that will display a custom menu or graphical UI using pfodApp on your Android mobile with no Android programming required. See the pfodDesigner tutorials for more details on creating menus, sub-menus, charts and graphical UI's.
If you don't want to use pfodApp, you can still use the pfodDesigner to design a menu and then program the menu cmds into UART control in Nordic's nRF Toolbox. This project also uses Nordic's nRF UART v2.0 app for testing.
Here Arduino version 1.8.19. Once the support is installed via Arduino 1.8.19 it will be available to Arduino V2 also
1. Start the Arduino IDE, V1.8.19
2. Go into
File → Preferences
3. Add
“https://sandeepmistry.github.io/arduino-nRF5/package_nRF5_boards_index.json”
as an "Additional Board Manager URL"
4.
Open the Boards Manager from the Tools -> Board menu and install
"Nordic Semiconductor nRF5 Boards" by Sandeepmistry. Latest
is version 0.8.0. This install is used to setup Arduino.
NOTE: During installation it takes the Arduino IDE a few minutes to extract the tools after they have been downloaded, please be patient.
1. cd <SKETCHBOOK>, where
<SKETCHBOOK> is your Arduino Sketch folder:
OS X:
~/Documents/Arduino
Linux: ~/Arduino
Windows:
~/Documents/Arduino
2. Create the following directories:
tools/nRF5FlashSoftDevice/tool/
3. Download
nRF5FlashSoftDevice.jar
to <SKETCHBOOK>/tools/nRF5FlashSoftDevice/tool/ (a local copy
of nRF5FlashSoftDevice.jar
is here)
4. Restart the Arduino IDE
1. Download the pfod_lp_nrf52_2024.zip
(Rev 13) file. (see pfod_lp_nrf52_2024.txt under hardware dir for
revisions)
2. Start the Arduino IDE, Open the File →
Preferences window an at the bottom find the directory where the
preferences.txt file
is stored. In Window's 7 you can click on that path to open the
directory in the Explorer.
3. From the preferences.txt directory,
open the packages
sub-directory.
4. Close the Arduino IDE
5.
Delete the entire sandeepmistry
directory.
6. Unzip pfod_lp_nrf52_2024.zip
to the packages
directory to install the pfod low power support.
This will install the modified sandeepmistry
directory.
That completes the Window 64bit installation.
For other operating systems, you need to install the appropriate
OpenOCD 0.11.0-1 binary.
1. From
https://github.com/xpack-dev-tools/openocd-xpack/releases/tag/v0.11.0-1/
download the OpenOCD binary for your OS and hardware.
2. Open the
packages/sandeepmistry/tools/openocd/0.10.0-dev.nrf5
directory and delete the bin
directory.
3. Extract the OpenOCD bin directory
from the downloaded binary and place it in the
packages/sandeepmistry/tools/openocd/0.10.0-dev.nrf5
to
replace the deleted directory.
Restart the Arduino IDE.
Open the Tools →
Board and scroll down to find the pfod
low power nRF52832 boards
Note: the boards with * against them do not use the
selected Programmer. Instead they use the pre-configured CMSIS-DAP
programmer.
Here we are using the *Generic nRF52832 bare
modules via DAPLink as the programmer.
There are many different 'bare' nRF52832 modules available and they expose different uC pins, and different numbers of pins, to the pcb edge connections. So to handle all of these possible configurations the *Generic nRF52832 boards just maps the nRF52832 uC pins to Arduino I/O, one to one. That is uC pin P0.00 is Arduino pin 0, P0.01 is pin 1 etc. You just need to use the pins you can access from the pcb connections. A0 to A7 are defined as the AIN pin numbers. D0 to D31 are not defined. Instead of D0 to D31 just use integers 0 to 31 for the digital I/O pin numbers.
By default Serial Tx, Rx are pins 29 (P0.29) and 30 (P0.30). While Wire SDA and SCL are defaulted to pins 9 (P0.09) and 10 (P0.10) and SPI MOSI, MISO and SCK default to 6 (P0.06), 7 (P0.07) and 8 (P0.08). SPI SS is not defined and is not needed for SPI master mode. You can use any I/O pin to control the chip select (CS / SS) of the connected SPI device.
If these default pins settings
are not convenient or not available on your module, you can override
them
with:-
Serial.setPins(rx_pin,tx_pin);
SPI.setPins(miso_pin,clk_pin,mosi_pin);
Wire.setPins(sda_pin,scl_pin);
Call these methods before call the respective begin() methods.
For the following pins P0.22. P0.23 .. through to P0.30, Nordic recommends that thay should be limited to low drive (standard, see High Drive Output Modes below), low frequency (<10kHz) I/O only. That would limit Serial on P0.29/P0.30 to 9600 baud (and exclude I2C/Wire usage), but for testing/development 115200 works. Since the UART uses significant supply current, you will not normally have it enabled in the final device. However if you do use Serial, either use 9600baud or use Serial.setPins to use another pair of pins.
There are two Generic nRF52832 boards to choose from. Generic nRF52832 (LFC crystal) and Generic nRF52832 (LFC RC osc) Some boards use the in-chip 32.768 kHz RC oscillator with an accuracy of about 1sec per hr at 25degC, while other boards have a 32.768Hz crystal which with gives better accuracy while using less current.
Start with the Generic nRF52832 (LFC RC osc) as it works for all boards, with or without an external crystal. Once you have that running you can try switching to Generic nRF52832 (LFC crystal), if your module does not have a crystal fitted the sketch will just hang in startup waiting for the crystal to start up.
The RC oscillator re-calibrates using the High Frequency (system) Clock, about once every 8sec and so uses a little more current ~2-3uA on average. The millis() and lp_timer uses the low frequency / low power clock, RTC, on the nRF52832. The timer accuracy depends on the accuracy of low frequency clock.
In the *Generic nRF52832 boards, the NFC pins are re-assigned as GPIO pins for general I/O. NFC (Near Field Communication) support is not included in this package.
The pins dedicated to the NFC antenna function (P0.09/P0.10) have increase leakage current (typically 2uA, max 10uA) between the two pins when they are used in GPIO mode, and are driven to different logical values. To save power the two pins should always be set to the same logical value whenever entering one of the device power saving modes. The Wire Stop condition sets both these pins high so no excess current is drawn when Wire is not reading/writing. You can use different pins for the Wire interface by calling Wire.setPins(sda_pin,scl_pin); before calling Wire.begin();
In this tutorial the GT832E_01 bare nRF52832 module is being used. This module has the advantage of 0.1” pcb pin spacing so you can attach headers to the pcb for easy connections. (GT832E_01 pins pdf version)
Connect the VDD 3.3V and Target
GND leads and the SWCLK and SWDIO leads connected to the GT832E_01
module.
When the SWCLK/SWDIO header is plugged in the
current monitor shunt resistors are shorted out to allow programming
to succeed without causing low chip volts.
On Windows 10 (and 7),
programming the nRF52832 can be a bit hit and miss.
NOTE:
When using a CMSIS-DAP programmer, even though the COM port shows up,
you may need to run the OpenOCD as described below each time you plug
the programmer in, to force it to be recognised fully.
The DAPLink
programmer does not seem to have this problem on Windows 10 and 7.
If the Upload of your Arduino
sketch fails try these steps.
Check the wiring to the
nRF52832. Wires on the correct pins and tight connections. If you get
“Error: Error connecting DP: cannot read IDR” the SWD
lead is not connected.
Plug the USB programmer into the
computer.
If the Arduino IDE programming fails, try it again
If still fails
Open a cmd prompt, run as admininstrator, and cd to the OpenOCD
dir
and and keep trying the cmd
bin\openocd
-f interface/cmsis-dap.cfg -f target/nrf52.cfg -c "telnet_port
pipe;tcl_port disabled;gdb_port disabled;log_output
until
you get the line Info : Listening on port 3333 for gdb connections
and the program is still running.
This can take 2 or 3 attempts
If
using a CMSIS-DAP programmer and multiple attempts of this cmd fail,
try using straight through connections without the 100 ohm protection
resistors, checking the connections carefully. The DAPLink
programming module is detected much more reliably.
The above cmd pipes the input from the current cmd terminal so you can just type the following command line to check the connection.
reset init
One user had problems with transferring from the programmer to battery operation. This does appears to be an atypical problem. However he solved this by powering the nRF52832 from the battery while programming. That is by only connecting the GND, SWCLK and SWDIO wires from the programmer. The 100 ohm series resistors in the SWCLK/SWDIO lines limit the current due to slight differences in the programmers 3V3 supply and the battery voltage.
With pfod_lp_nrf52_2024 the softdevice is automatically programmed each time you upload a sketch.
NOTE: Often you will need to remove
the chip protection BEFORE programming the Nordic Softdevice.
Before you can run a sketch you need to
first load a 'softdevice' into the nRF52832 chip. The project uses
the s132 softdevice, s132_nrf52_2.0.1_softdevice.hex
(http://www.nordicsemi.com/eng/content/download/95151/1606944/file/s132_nrf52_2.0.0.zip)
The pfod_lp_nrf52.zip already includes s132, so you don't need to
download it from the Nordic website. However you still need to
program it into your nRF53832 chip.
Wire up the GT832E_01 module power
and SWCLK/SWDIO as described above.
The
nRF52 chip SWDIO line has an internal pull-up resistor and the SWDCLK
line has an internal pull-down resistor.
Start the Arduino IDE and make sure CMSIS-DAP is the
selected programmer (both CMSIS-DAP or the DAPLink programmers use
CMSIS-DAP) and that the mbed COM port is selected as the Port, i.e.
COM41
Then use the Tools → nRF52
Flash SoftDevice to install the softdevice
Note Use
the nRF52 Flash SoftDevice option
half way down the Tools menu. Do
not use the Burn Bootloader option at the bottom. The soft device
flashed is the one selected under the Softdevice: The
pfod_lp_nrf52.zip has pre-configured S132 as the softdevice for all
the nRF52832 Boards in the pfod low power nRF52832 menu section.
NOTE: Arduino
sometimes looses track of the COM ports. If you have problems
uploading your sketch, close Arduino, un-plug the USB cable from the
computer, restart Arduino and plug the USB back in. If there is an
error recognizing the USB connection, restart the computer.
Also
see Help My Upload Failed above.
NOTE: If the flash of the
SoftDevice fails, your nRF52 module may be protected against
re-programming. See Removing the
nRF52 coding protection flag and programming the sketch
below
Otherwise if you get an error msg Error:
Target not examined yet near the top of
the Arduino programming output, check the wiring to your nRF52
module.
The VDD must rise quickly 0 to 1.7V is less than 60ms and be noise free.
The
nRF52832 may not startup correctly if the rise time for the supply
(VDD) from 0V to 1.7V is greater then 60ms, so if you are supplying
the nRF52832 from a very low current source via a large capacitor you
should add a supply monitor IC to keep the nRF52832 off until the
supply voltage reaches 3V3 and the capacitor has sufficient charge to
supply the ~20mA for 20ms while the chip starts up.
See Remote
Controlled Light Switch for an example.
Also
from
the chip spec, “A step increase in supply voltage of 300 mV or
more, with rise time of 300 ms or less, within the valid supply
range, may result in a system reset”. A clean power cycle
should exit debug mode.
This note refers to the nRF51 and another comment suggests this problem is fixed in the nRF52, but if you are having problems try grounding the SWCLK pin after programming. The recommended 1K resistor between there and the programmer, see the Programmer Circuit above, will protect the programmer.
NOTE:
The nRF52 can un-expectedly enter debug mode while running, due to
noise on the SWCLK line, resulting in a few mA of supply current
instead of 100uA. See Issue
with NRF51 unexpectedly entering debug mode
The solution
appears to be to add a small value resistor say 470R and a small
capacitor say 1nF in parallel between SWCLK pin and GND as close to
the chip as possible and to disconnect any long traces connecting to
SWCLK, after you have programmed the chip.
“As a last resort
you might even consider connecting a spare GPIO on the nRF51822
directly to SWCLK, and set the spare GPIO to output low after power
up. This will prevent re-programming, so you might want to have some
means for the application to release the pin”
OR
cut the board connection between the GPIO and SWCLK
I tested a 1nF between SWCLK and GND and was unable to program the chip with the capacitor in place, but the chip continued to run and could be connected to. Even after removing the capacitor, the chip would not program. Re-flashing the Softdevice worked and made the chip programmable again. So do not add the suppression capacitor/resistor until after you have finished programming. My final solution is to connect a wire from SWCLK to GND, to ground it, after programming.
The trick to getting a really low power solution is to do
nothing most of the time, minimize the current through external
pull-up/pull-down resistors on inputs and don't have any extra
components.
Also see Optimizing
Power on nRF52 Designs (local
copy here)
NOTE: If your supply current is not as low as you would
like/expect, check that
a) the SWD/SCLK cables have been
removed
b) the board has been powered cycled
c) Serial.begin()
has NOT been called
d) the TX/RX serial cables are removed
Normally in Arduino you put all your action code in the loop() method which is then called repeatedly by Arduino, but this means most of the time the processor is just spinning around doing nothing.
Consider the loop() method in the following nRF52832_basic_blink.ino example that blinks the led pin at 10Hz. Note: The Arduino 1.8.19 Blink example will not compile as there is no LED_BUILTIN on bare nRF52832 modules.
int led = 14; // the pin, P0.14, an external led is connected to // the setup routine runs once when you press reset: void setup() { // initialize the digital pin as an output. pinMode(led, OUTPUT); } // the loop routine runs over and over again forever: void loop() { digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level) delay(50); // wait for a 50ms digitalWrite(led, LOW); // turn the LED off by making the voltage LOW delay(50); // wait for a 50ms }
Arduino just runs this loop() code for ever, setting the led on, delaying for 50ms and then turning it off for 50ms.
Having programmed the nRF52832_basic_blink.ino sketch, lets measure the supply current.
1) Upload the
nRF52832_basic_blink.ino
sketch onto the GT832E_01, via the SWCLK/SWDIO header. With this
header plugged in the current monitoring resistors are shorted out
allow programming to succeed. Do not connect the Tx/Rx leads to the
GT832E_01 target.
2) Remove the USB 5V supply supplying the
GT832E_01 via the 3v3 regulator to reset the nRF52832 after the
programming is finished. This is necessary in order to measure the
correct supply current.
3) Move the current shunt shorting link so
that it shorts out the 2K2 resistor leaving only the 300R resistor in
the ground line. For very low current measurements just remove the
link completely
4) Remove SWCLK/SWDIO header. Important:
Leave this header inserted when
power cycling the nRF52 after programming to ensure it starts up
correctly. The nRF52 draws ~20mA for ~20ms on startup. Important:
Remove
this SWCLK/SWDIO header after the nRF52 starts up as it draws ~100uA.
Also remove the Serial Tx/Rx header if connected.
Connection your multimeter, set to Hz, to pins 14 and GND should read 10Hz
Sometimes Arduino looses connection to the programming COM port. In that case try unplugging the programmer USB cable from the computer and plug it in again. If that does not work, close the Arduino IDE and restart it.
For the
nRF52832_basic_blink.ino,
sketch the supply current is about 5mA (with no led connected between
P0.14 and GND). The voltage measured across the 300R current shunt
resistor is ~1.53V. The voltage measured across the 300R current
shunt resistor is ~1.52V, So the supply current is
1.53 Volts /
300 ohms == 0.005 Amps (i.e. 5mA)
If the supply current is much higher than this, there not enough voltage left to power the chip. This sets the upper limit on the supply current that can be measured with a 300R shunt resistor.
NOTE: If the oscilloscope / voltmeter reads 2.6V across the current measuring resistors than the nRF52 chip is not running, but is stuck in start up due to insufficient supply current.
Looking inside the delay() method you will see
void delay( uint32_t ms ) { if ( ms == 0 ) { return ; } uint32_t start = millis() ; do { } while ( millis() - start < ms ) ; }
The do{ }while loop just spins using up processor time, and power, until the millis() counter has incremented by ms. This is just a waste of time and prevents your sketch dealing with any triggers / inputs that occur while running your uC as fast as it can.
Don't use delay()
For normal Arduino coding you should use a timer library, like millisDelay. See How to code Timers and Delays in Arduino for all the details. However while millisDelay keeps the loop() running as fast as it can, the uC is still continually running using lots of current. For low power you need to use a low power timer instead.
As mention above, The trick to getting a really low power solution is to do nothing most of the time.
In the nRF52832_basic_blink.ino, above, most of the time the uC is just stuck in a tight loop waiting for the delay() to expire. So to reduce the supply current we want to put the micro-processor to sleep until there is something to do.
Here is low power version Blink using lp_timer, Blink_lp_timer_GT832E_01.ino
#include <lp_timer.h> int led = 14; // Use P0.14 has the 'led' pin bool ledOn = false; lp_timer ledTimer; const unsigned long DELAY_TIME = 50; // ms == 10Hz void setup() { pinMode(led, OUTPUT); ledTimer.startTimer(DELAY_TIME, handleLedTimer); } void loop() { sleep(); // just sleep here waiting for the timer to trigger } void handleLedTimer() { ledOn = !ledOn; // toggle state if (ledOn) { digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level) } else { digitalWrite(led, LOW); // turn the LED off by making the voltage LOW } }
After each DELAY_TIME, the ledTimer triggers and wakes up the loop() from the inside the sleep(). The sleep() method, on waking, calls the handleLedTimer method and then when sleep() exits, the rest of the loop(), if any, is executed. Finally when loop() is called again, it goes back to sleep waiting for the next trigger.
It is important to note that the handleLedTimer method is NOT an interrupt routine. Rather it is normal sketch method called from sleep() from with-in the sketch's loop() method. This means the handleLedTimer method has access to all the sketch's variables and methods, without needing to use volatile variables or any multi-tasking locks. This is true for all the handler methods used in this low power library.
Measuring the supply current for this sketch using the 2K2 + 300R == 2K5, i.e. with the shunt link completely removed, gives meter readings of about 22mV, which implies an average supply current of 9uA (0.022V / 2500 ohms) = 0.009mA
So using sleep() and the lp_timer() has reduce the supply current from ~6mA to ~0.009mA (~9uA, with no led attached)
The lp_timer class provides a means set a time out and call the handling method either once off, startDelay(), or repeatedly, startTimer(). As well as running the loop() method each time it times out.
In this
library, the nRF52's RTC counter has been configured to give ms
timings, with a maximum time out of ~68mins. Time outs greater than
4095000ms (68min 15 sec) will be limited to 4095000ms. The minimum
timeout is 2ms. Requests for timeouts of 0ms or 1ms will result in a
2ms timeout.In
pfod_lp_nrf52_2023, Rev 11, the nRF52's RTC counter is configured to
count 30.5us increments. The maximum timeout is now 511secs and the
minimum is 5 ticks =>152.5us.
startDelay()
and startTimer()
continue to take ms arguments but scale them by 1000 and call the new
startDelay_us()
and startTimer_us()
methods. Call startDelay_us()
and startTimer_us()
directly, in preference, to avoid the times 1000 multiplication. To
revert to the previous tick size comment out the #define
LP_TIMER_US line the
utility/lp_timer_speed.h file.
For longer timeouts use an hour time out to count the hours in the handler and then at the end start a minutes, secs, ms timer to finish off. See the long_lp_timer.ino example which supports timers of up to 245,000 years.
Note: The millis(), micros() methods also uses the same timer as lp_timer. As a result the resolution of the micros() call is 30.5us. That is calls to micros() will increment in multiples of 30.5us.
You will see in the on-line nRF52 forums references to enabling the nRF52 DC to DC converter and enabling/disabling peripherials. See Optimizing Power on nRF52 Designs (local copy here) The DC to DC converter option requires extra components, which boards like the NanoV2 has but, which 'bare' nRF52 boards, like GT832E_01 and SkyLab, will not, so these sketches don't use it.
As for the peripherials such as Serial (UART), SPI and Wire. These are disabled until you call .begin() and then disabled again when you call .end(). So for lowest power usage call .end() for SPI and Wire when you are not using them.
Extra components, leds, sensors (accelerometers), etc, use power and when the basic chip with BLE is only using 74uA, even “low power” leds and sensors have a significant impact on the supply current. This is why you need to build your low power BLE project using a bare module rather than a development board.
The GT832E_01 has no extra external components. You need to provide a 3.3V supply. The MAX8881 3.3V regulator used on this programming/test board uses typically 3.5uA and its supply current is included in the measurements.
The internal pull-up/pull-down resistors are extra components that used extra power. They are typically 13K ohms (11K to 16K). These need to be taken into account when calculating the supply current drawn by the external circuit connected to this pin. Grounding a pin with a pullup resistor ( INPUT_PULLUP) draws an additional 250uA. Similarly for connecting a pin, with a pulldown resistor (INPUT_PULLDOWN), to VDD. To minimize the extra current, use larger external resistors instead of INPUT_PULLUP or INPUT_PULLDOWN.
However unconnected inputs should be set to INPUT-PULLUP so the input does not float and wake up the chip (see this link). Setting INPUT-PULLUP is only necessary if your code changes a pin to an input and there is not an external pullup resistor attached.
The internal pull-up/pull-down resistors, if used, together with the external circuit components, also needs to be taken into account when calculating the voltage applied to a comparator pin input.
For debugging you can use the usual Arduino Serial prints. The low power sketches shown here generally don't use Serial except for debugging, because the Arduino Serial connection uses noticeably more power.
The Generic nRF52832 board defaults to P0.29 and P0.30 as the Serial Tx, Rx , but you can override that in your sketch using Serial.setPins(rx_pin,tx_pin); before calling Serial.begin();
void setup() { Serial.setPins(11,12); // remap Rx to P0.11 and Tx to P0.12 Serial.begin(115200); . . .
The
Blink_lp_timer_debug_GT832E_01.ino
sketch does that to remap Serial Tx to P0.12 and Serial Rx to
P0.11
Plug in the Tx/Rx header and connecting “to
Target Tx” lead to P0.12 and the “to Target Rx”
lead to P0.11 lets you display the Serial output on your computer
There is some sample output from Blink_lp_timer_debug_GT832E_01.ino
startup() completed at 1ms loop() woke up at 4ms H loop() woke up at 51ms L loop() woke up at 101ms H loop() woke up at 151ms L loop() woke up at 201ms H loop() woke up at 251ms L loop() woke up at 302ms H
It is important to remember that the loop() is woken up by triggers other then the one you explicitly code in your sketch. The handleLedTimer is called from sleep() just before waking up the loop().
cprint... methods let you print debug messages from within C (and C++) files to Serial. To use cprint() and cprintNum() to debug the C code files or .cpp files, add
#ifdef __cplusplus extern "C"{ #endif // __cplusplus void cprint(const char* str); void cprintNum(const char* str, const uint32_t num); #ifdef __cplusplus } // extern "C" #endif
to the top if the file and call them when you want to output
debug msgs, Also in your main sketch (.ino), define cprint and
cprintNum as
extern "C" void cprint(const char* str) { Serial.println(str); } extern "C" void cprintNum(const char* str, uint32_t num) { Serial.print(str); Serial.print(' '); Serial.println(num); }
NOTE: Keep the debug strings short and don't try to print from inside a CRITICAL_REGION_ENTER(); CRITICAL_REGION_EXIT(); block of code.
Your timer handlers, etc are executed in the same context as your
sketch (instead of in an interrupt context). As they are triggered
they are queued to be called from sleep(), when your loop() wakes up.
This queue has a fixed length (default 20) which is defined in
lp_timer_init.h If the triggers fire faster then your sketch
can handle them, then the queue will fill up and you will loose some.
You can check maximum queue usage by uncommenting
#define
SCHEDULER_PROFILER
at the top of app_scheduler.h and then
calling app_sched_queue_utilization_get(); in you sketch code.
You can also call app_sched_queue_utilization_clear(); to clear the maximum back to zero and start checking again.
See Low Power Button Debounce below for an example of using this.
There a lots of BLE services defined by the BLE standard, but a replacement for the the “Classic Bluetooth” Serial Port Profile (SPP) is not one of them. This has meant manufactures have been left to define their own version of a BLE UART service. RFduno, RedbearLab, BLUNO, HM-10 and Nordic (maker of the nRF52 chips) all have their own unique UART service. pfodApp will connect to all of these services, but other apps' support for these BLE UARTs is limited.
A commonly used service is Nordic's UART Service and that is the one that this library uses. Nordic provides a number of apps that will connect to that service, e.g. Nordic's nRF Toolbox and Nordic's nRF UART v2.0. (MicroBit's original implementation of Nordic's Service/Characteristics had the TX and RX swapped. pfodApp connects to that from as well.)
Here is the Blink_lp_BLE_GT832E_01.ino code that lets you turn the blinking led on and off via BLE
#include <lp_BLESerial.h> #include <lp_timer.h> int led = 14; // Use P0.14 has the 'led' pin bool ledOn = false; lp_timer ledTimer; const unsigned long DELAY_TIME = 50; // ms = 10Hz lp_BLESerial ble; // the setup routine runs once when you press reset: void setup() { // initialize the digital pin as an output. pinMode(led, OUTPUT); ble.setName("Led Control"); // set advertised name, default name is "Nordic BLE UART" ble.begin(); // start advertising and be ready to accept connections ledTimer.startTimer(DELAY_TIME, handleLedTimer); } void loop() { sleep(); // just sleep here waiting for the timer/BLE to trigger // check for new BLE cmd 'a' starts blinking, 'b' stops blinking while (ble.available() ) { int i = ble.read(); if ('a' == i) { ledTimer.startTimer(DELAY_TIME, handleLedTimer); // start blinking } else if ('b' == i) { ledTimer.stop(); // stop blinking digitalWrite(led, LOW); // turn off } } } void handleLedTimer() { ledOn = !ledOn; // toggle state if (ledOn) { digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level) } else { digitalWrite(led, LOW); // turn the LED off by making the voltage LOW } }
When the sleep() is triggered to wake up, the loop() checks if there is a new command from the BLE connection and starts/stops the blinking. Note: the sleep() is triggered to wake up by multiple things, the timer, BLE connection, disconnection, receive data, etc, so you need to check if any data has been received.
The Nordic's nRF UART v2.0 app is used to test this connection. After connecting you can send a or b to start or stop the blinking.
With the blinking off, the
oscilloscope reads, across the 2K5 shunt, ~163mV(avg) advertising and
~148mV(avg) connected. No Led connected.
That is ~65uA
advertising and ~59uA connected. Important: Remove
the SWCLK/SWDIO header after the nRF52 starts up as it draws an
additional ~100uA
The supply current used by the BLE connection is controlled by the TX power, the advertising interval and the connection interval. The defaults used here are set in bleConstants.h and are TX power +4 (maximum available), advertising interval 500ms and connection interval 100ms min. to 150ms max. If you make the TX power lower using lp_BLESerial.setTxPower() (e.g. ble.setTxPower(-8); ), or make the advertising interval longer using lp_BLESerial setAdvertisingInterval() (e.g. ble.setAdvertisingInterval(1000); ) or make the connection intervals longer using lp_BLESerial.setConnectionInterval() (e.g. ble.setConnectionInterval(200,250); ), then the supply current needed will be less. However just turning off the advertising is a much more effective means of reducing the supply current.
As mentioned above the trick to very low power BLE is to do nothing most of the time. In most use cases the BLE device spends most of its time waiting for a connection and then spends a relative short time sending the data and then disconnects and goes back to waiting. In the example above, Blink_lp_BLE_GT832E_01.ino, the device is advertising while ever it is not connected. This is the default. However this means the device is using ~71uA most if the time. This current can be dramatically reduced by turning off advertising and “doing nothing” most of the time. Rev 6 of the pfod_lp_nrf52_2002 code adds methods to set the advertising timeout, and start/stop the advertising.
The two new methods are :-
void
setAdvertisingTimeout(uint16_t sec); which
sets how long the advertising will run after it is started and
void
setAdvertising(bool on); which
turn adverting on true or
off false.
The modifications to Blink_lp_BLE_GT832E_01.ino are contained in LowerPower_Blink_GT832E_01.ino They are simple.
lp_timer BLE_AdvertisingRestartTimer; // the advert restart timer const unsigned long BLE_ADVERTISING_ms = 20 * 1000;// restart advertising every 20sec const uint16_t BLE_ADVERTISING_TIMEOUT_secs = 2;// run advertising for 2 seconds // pfodApp needs at least 2sec of advertising to scan and connect void restartAdvertising() { // called by timer every 20seconds ble.setAdvertising(true); // ignored if currently connected } void setup() { pinMode(led, OUTPUT); ble.setName("Led Control"); // set advertised name, default name is "Nordic BLE UART" ble.setAdvertisingTimeout(BLE_ADVERTISING_TIMEOUT_secs); // 2 sec BLE_AdvertisingRestartTimer.startTimer(BLE_ADVERTISING_ms, restartAdvertising); // restart advertising every 20sec for 2sec ble.begin(); // start advertising and be ready to accept connections ledTimer.startTimer(DELAY_TIME, handleLedTimer); }
In setup() set the advertising timeout to 2 seconds before calling lp_BLESerial.begin() ( ble.begin(); ) and start a timer to trigger every 20seconds to restart the advertising for another 2 seconds. With these modifications, when not connected, for 2sec, while advertising the supply current is ~65uA. While for 18secs when not advertising it is about ~12uA for an average of ~17uA.
The downside is that most applications, like Nordic's nRF UART v2.0 and pfodApp, need to scan the device's advertising prior to connecting, so it can take upto 18 seconds before the device is scanned and can be connected. If the advertising on for less than 2 seconds then pfodApp does not reliably detect the device. If the interval between advertising restarts is greater than about 30 seconds most apps will have terminated their scanning procedure, so 2 seconds on in a 20 second cycle seems close to ideal.
As well as receiving cmds, lp_BLESerial can also send data, but only 20 bytes in each packet and up to 4 packets per connection interval. If you try and send too much data too quickly it can get lost.
pfod_lp_nrf52_2022 Rev 6 changes the
way the BLE UART works. In Rev 6 the tx speed is increased by a
factor of 4 and the lp_BLESerial print statements will now
block if the Tx buffer is full. Previously excess chars were just
dropped. You should avoid sending data so fast that the print
statements block as this will interfere with the running of the rest
of the code as is also the case in normal Arduino sketches. Your code
can check is there is sufficient space in the Tx buffer by
calling
size_t lp_BLESerial.availableForWrite();
The
suggested way to do this is in a timer method. If not enough space is
available then skip the write and check again next time the timer
fires.
lp_BLESerial buffers the BLE writes and then releases them 4 x 20 bytes at a time at connection interval (default in the range 100ms to 150ms). The lp_BLESerial constructor allows you to specify the size of the Tx buffer. The default is size 1024 bytes. If the device is not connected then all print/write output is discarded. The Tx buffer is also cleared when the device disconnects.
This examples sends the chip temperature once a sec when connected. The nRF52 chip has an on-board temperature sensor, accessed via getChipTemperature() and getRawChipTemperature(). It has a resolution of 0.25 deg C and is not particularly accurate (+/- 2.5 deg C between -40C to +85C) , but if you calibrate it and apply the calibration factors in the sketch, then it could be used as a room temperature monitor. Historical temperatures could be stored and then sent when requested. The handleTempTimer() only runs once per sec and so will not overfill the Tx buffer and block.
This example code, lp_BLE_temp_GT832_01.ino, does not do any calibration or storage of measurements. It just sends the temperature once a second when connected. This example also illustrates connection/disconnection handlers to start and stop timers, but they are not really needed as ble.print() will discard any bytes written while not connected and the Tx buffer is cleared on disconnection.
#include <lp_BLESerial.h> lp_timer tempTimer; const unsigned long DELAY_TIME = 1000; // ms == 1sec lp_BLESerial ble; void setup() { ble.setName("Chip Temperature"); // set advertised name, default name is "Nordic BLE UART" ble.setConnectedHandler(handleConnection); // when a connection is made ble.setDisconnectedHandler(handleDisconnection); // when the connection is dropped or goes out of range ble.begin(); // start advertising and be ready to accept connections } void loop() { sleep(); // just sleep here waiting for a trigger while (ble.available()) { int i = ble.read(); // clear BLE recieve buffer and ignore // could add cmds there to send stored historical temps } } void handleTempTimer() { // send the current time and temp float temp = getChipTemperature(); ble.print(millis()); ble.print(','); ble.print(temp); ble.println(); } void handleConnection(BLECentral& central) { // could just start this in setup and leave running tempTimer.startTimer(DELAY_TIME, handleTempTimer); } void handleDisconnection(BLECentral& central) { // no real need to stop here, ble.print discards the bytes if not connected tempTimer.stop(); }
In addition to the usual Arduino functions and the lp_BLESerial and getChipTemperature, this low power library has a low power pin voltage comparator that can used on pins AIN0 (A0) to AIN7 (A7). That is pins P0.02, P0.03, P0.04, P0.05, P0.28, P0.29, P0.30 and P0.31. This provides low power triggers to wake up your sketch when the input voltage on a pin changes level.
This example, lp_BLE_comparator_GT832E_01.ino, sets up a BLE Nordic UART and pin 2 as an input with a INPUT_PULLDOWN and then starts monitoring for voltage changes on pin 2 compared to a voltage level of ½ Vdd (i.e. 8/16 * Vdd)
Note: The internal pull-up/pull-down resistors are typically 13K ohms (11K to 16K) These need to be taken into account when calculating the voltage applied to the comparator and the current drawn by the external circuit connected to this pin.
#include <lp_BLESerial.h> #include <lp_comparator.h> int led = 14; // Use P0.14 has the 'led' pin bool ledOn = false; int comparatorPin = A7; // P0.31 lp_BLESerial ble; void setup() { pinMode(comparatorPin, INPUT_PULLUP); // set compare pin with INPUT_PULLUP or INPUT_PULLDOWN to prevent floating pin triggers // but the internal pullup/pulldown resistor is ~13K which draws an extra 254uA when INPUT_PULLUP is grounded or INPUT_PULLDOWN is connected to Vdd // for very low power use pinMode(2, INPUT) and supply a high value external pullup or pulldown resistor e.g. 100K draws ~33uA pinMode(led, OUTPUT); // initialize the digital pin as an output. ble.setName("Pin Change"); // set advertised name, default name is "Nordic BLE UART" ble.begin(); // start advertising and be ready to accept connections lp_comparator_start(comparatorPin, REF_8_16Vdd, handlePinLevelChange); // always triggers pin LOW first, then if pin HIGH, will trigger HIGH } void loop() { sleep(); // just sleep here waiting a trigger while (ble.available()) { int i = ble.read(); // clear BLE recieve buffer and ignore } } // called when pin changes state, pinState is state detected, // HIGH when Above and LOW when Below the reference voltage void handlePinLevelChange(int pinState) { if (ble.isConnected()) { ble.print(millis()); ble.print(','); ble.print((pinState == HIGH) ? 'H' : 'L'); ble.println(); } digitalWrite(led, pinState); // turn the LED on when Above }
When the input pin is open circuit, the internal pull down resistor keeps the pin voltage low and the led off. In this state the sketch uses less than 100uA both when advertising and when connected. However when the input is connected to a voltage > 0V, current flows through the pull down resistor. The internal pull-down resistor is ~13K which draws an extra ~250uA, if the input in connected to Vdd. To avoid this excessive current, you should leave the pinMode at its default startup setting ( pinMode(2, INPUT) ) and then provide your own external high value pull-up or pull-down resistor, say 100K (~33uA). The downside of this approach is that the input is now more susceptible to noise pickup.
Using the Nordic UART app for testing, connect and then use a jumper lead to connect D2 to Vdd. You will see a lot of output scroll by. This due to the poor electrical connection as you push the jumper onto the pin. A lot of triggers happen in a short space of time and the ble.prints are buffered, up to 1024 bytes and then sent to your mobile at a slower rate.
The Low Power nRF52832 library takes special care to ensure a flood of triggers from a noisy comparator input does not prevent other triggers from being processed. The lp_comparator triggers are placed on a separate queue to the timer and BLE triggers. The queue is only 4 slots deep, but the code ensures that the if the queue fills up then the last trigger is continually updated with the latest comparator result. This ensures that when your code has processed all the lp_comparator triggers, the last one processed is the current state of the pin. An artefact of this code is that your handler can receive two successive HIGH or two successive LOW triggers, when clearly the pin must have changed state in between.
You can have lp_comparator and lp_ADC running at the same time as long as they are monitoring different ADC pins. Calling lp_comparator_start( ) while it is already running will return a non-zero error code. Use lp_comparator_isRunning() (introduced in Rev 11) to check if the lp_compartor is running.
The nRF52832 can also trigger on input pin H/L transitions, but it can miss transitions when the chip is handling BLE functions, so lp_pinChange support has not been provide in this implementation. Instead use the lp_comparator with a ½ Vdd reference.
Rev 9 of the pfod_lp_nrf52, adds a non-blocking version of ADC (an analogRead replacement). The standard blocking analogRead(..) method is still available and the standard configuration methods analogReadResolution(..) and analogReference(..) also configure lp_ADC. The default ADC sample time has been increased to 10uS to accommodate the higher source resistances found on low power circuits. The 10uS sample time is suitable for source resistances upto 100K, e.g a 200K / 200K input voltage divider.
See the example sketch lp_ADC_test.ino
Calling lp_ADC_start( ) starts the conversion and calls back the handler with the count as an argument when the conversion completes.
lp_ADC_calibrate(); // if you want the offset calibrated before taking this sample. uint32_t err = lp_ADC_start(28, handle_ADC_result); // pin 28 on BareBoard nRF52832 == A4 // this callback method is called on the loop() thread void handle_ADC_result(int count) { Serial.print("ADC result woke loop "); Serial.print((count * 3.0) / 1023); Serial.print("V -- "); }
The callback method is called on the loop() thread so no
synchronisation or volatile variables are required. You can call
lp_ADC_start( ) again directly from the handler if required.
The
lp_ADC_calibrate() method can be called from the loop() at any
time and sets a flag to perform an single offset calibration for the
ADC before the next sample is taken.
The calibration flag is
cleared by the lp_ADC_start( ). If lp_ADC_calibrate()
can be called immediately after calling lp_ADC_start( ), the
calibration is done before the next sample after the current sample
returns. Normal samples take about 40us (non-blocking). Samples with
calibration take about 140us (non-blocking)
The lp_comparator and the lp_ADC can be used at the same time provided they are on different analog pins.
The nRF52 outputs set using the pinMode( .. , OUTPUT) have
'Standard' drive capability when driving low '0' and high '1' i.e.
S0S1.
Standard drive can sink (output low) 2mA (typical) if the
supply voltage is >1.7V (min 1mA if supply voltage is
>1.7V)
Standard drive can source (output high) 2mA (typical) if
the supply voltage is >1.7V (min 1mA if supply voltage is >1.7V)
The nRF52 also has two other output modes, High Drive (H0 and H1) and Disconnect (D0 and D1)
High Drive can sink (output low) 10mA (typical) if the supply
voltage is >2.7V (min 3mA if supply voltage is >1.7V)
High
Drive can source (output high) 9mA (typical) if the supply voltage is
>2.7V (min 3mA if supply voltage is >1.7V)
The Disconnect options can be used to disconnect the pin from the output driver in either the low '0' or high '1' state. This is useful when driving busses that expect an open collector driver, like I2C
The following additional pinMode settings can be used to set High Drive and Disconnect for either low '0' or high '1' outputs (or both)
OUTPUT_S0S1 – Standard drive low and high. This is
the same as pinMode OUTPUT
OUTPUT_H0S1 – High
drive low, Standard drive high
OUTPUT_S0H1 – Standard
drive low, High drive high
OUTPUT_H0H1 – High drive
low, High drive high
OUTPUT_D0S1 – Disconnected when
low, Standard drive high
OUTPUT_D0H1 – Disconnected
when low, High drive high
OUTPUT_S0D1 – Standard
drive low, Disconnected when high
OUTPUT_H0D1 – High
drive low, Disconnected when high
There are a number of different versions of the nRF52832 chip. Each with there own set of bugs. Rev 5 of pfod_lp_nrf52 includes nrf52ChipInfo methods to display the chip version.
Example sketch
#include <nRFChipInfo.h> void setup() { // put your setup code here, to run once: Serial.begin(115200); for (int i=10; i>0; i--) { Serial.print(i); Serial.print(' '); delay(500); } Serial.println(); Serial.print("Part: nRF"); Serial.println(nRF52PartNo(),HEX); Serial.print("Variant: "); Serial.println((char*)nRF52Variant()); Serial.print("Ram: "); Serial.print(nRF52RamKb()); Serial.println("Kb"); Serial.print("Flash: "); Serial.print(nRF52FlashKb()); Serial.println("Kb"); Serial.println("Setup finished"); } void loop() { // nothing here }
Example Output from the ChipInfo sketch for NanoV2
Part: nRF52832 Variant: AAE1 Ram: 64Kb Flash: 512Kb Setup finished
How to decode the variant. (Note: some version of the AB variant
return incorrect values Ram: 64K and Flash: 512K instead of the
correct values, 32K, 256K)
prefix Flash Ram AA 512 64 AB 256 32 suffix [A . . Z] Hardware version/revision identifier (incremental) [0 . . 9] Production device identifier (incremental)
A common requirement is to monitor a push button input and ignore the button contact bounces. The sketch, lp_BLE_debounce_GT832E_01.ino, illustrates how to write a low power button debounce.
#include <lp_BLESerial.h> #include <lp_comparator.h> #include <lp_timer.h> int led = 14; // Use P0.14 has the 'led' pin bool ledOn = false; int comparatorPin = A7; // P0.31 lp_BLESerial ble; lp_timer debounceTimer; uint32_t debounceTimeOut = 20; // ms int lastButtonState = -1; // not set initially int buttonState = -1; // not set initially void setup() { pinMode(comparatorPin, INPUT_PULLUP); // set compare pin with INPUT_PULLUP or INPUT_PULLDOWN to prevent floating pin triggers pinMode(led, OUTPUT); // initialize the digital pin as an output. ble.setName("Button Debounce"); // set advertised name, default name is "Nordic BLE UART" ble.begin(); // start advertising and be ready to accept connections lp_comparator_start(comparatorPin, REF_8_16Vdd, handlePinLevelChange); // always triggers pin LOW first, then if pin HIGH, will trigger HIGH } void loop() { sleep(); // just sleep here waiting for a trigger while (ble.available()) { int i = ble.read(); // clear BLE recieve buffer and ignore } } // called when pin changes state, pinState is state detected, HIGH or LOW void handlePinLevelChange(int pinState) { if (pinState != lastButtonState) { lastButtonState = pinState; debounceTimer.stop(); // stop last timer if any debounceTimer.startDelay(debounceTimeOut, handleDebounceTimeout); } } void handleDebounceTimeout() { buttonState = lastButtonState; // input has settled // ble.print("maxQ:"); ble.print(app_sched_queue_utilization_get()); //needs #define SCHEDULER_PROFILER in utility/app_schedule.h ble.print(' '); ble.print((buttonState == HIGH) ? 'H' : 'L'); digitalWrite(led, buttonState); // turn the LED on when input HIGH }
Each time the lp_comparator triggers, a delay timer is started. When it times out the push button contacts have settled. Note that the button state is initially -1. When the lp_comparator is started, lp_comparator_start, it always first fires a LOW trigger and then if the input is actually high it follows that with a HIGH trigger. This initializes the button state on startup.
The timer triggers are placed on a separate queue from the lp_comparator (and BLE) triggers. As well as the time out trigger, each timer stop and start puts a trigger on the queue. The sketch just ignores these stop/start triggers, but they do take up space on the queue so you might be concerned that a noisy pin input would generate a lot of handlePinLevelChange inputs and so generate a lot of stop/start timer triggers and overload their queue. It turns out this does not happen in this low power library, because the lp_comparator trigger queue is limited to 4 slots and the if (pinState != lastButtonState) filters a lot of the noise.
However you may want to check that your sketch is not missing any triggers due to a full queue. app_sched_queue_utilization_get() lets you do this. To enable this check, in utility/app_schedule.h under arduino's package/sandeepmistry/hardware directory, un-comment the #define SCHEDULER_PROFILER line. You can then call app_sched_queue_utilization_get() to see the maximum number of slots that were used of the 8 configured in lp_timer_init.h Enabling this check shows that only 1 or 2 slots were ever used with a very noisy input. An actual push button is much cleaner.
You can use the free pfodDesigner Android app to create your own custom control menus/sub-menus and log and plot data and then have pfodDesigner generate the Low Power sketch for you. You will need to use pfodApp to connected and display the menu you created, but no Android programming is required. pfodApp handles all of that for you.
There are lots of tutorials on using pfodDesigner. Here we will create a button to pulse the GT832E_01's led on for 2sec, and another menu item to display the voltage read on the A7 pin (P0.31). A third button will open a chart of the A7 voltage readings, which are also saved to a log file on your mobile.
Install pfodDesignerV3 rev 3.0.3875+ from Google Play to your Android mobile. Start a new Menu and click on the Target button and then select Bluetooth Low Energy (BLE) and then Generic nRF52832 bare modules as the target. Use your mobile's back button to get back to the Menu Edit screen.
Once the Generic nRF52832 bare modules is selected, when you go to connect an digital I/O pin to a menu item, pfodDesignerV3 displays a list of the P0... pins.
Choose a pin that is exposed by your module. Here pin P0.14 is selected to drive the external led output.
Then follow this tutorial to set PO.14 to pulse High for 2 secs.
Then add a Data Display menu item to display
the value of the analogRead of A7, as described in this
tutorial. Also, following
that tutorial, add a Chart Button and set up a Plot to plot the
A7 values. This also logs the readings to a log file on your
mobile.
Note: The default ADC scale
is 0 to 3.0V using the internal reference of 0.6 and a gain of 5 ==
3.0V full scale and the resolution is 10 bits ie. 0 to 1024
So
set Edit Display Max
to 3.0 when
setting up the analogRead and plot of A7
Finally, use the Generate Code button to generate the Arduino low power sketch for the nRF52832 module. Here is an example menu design, lp_BLE_GT832E_01_example.ino It displays this menu when pfodApp is used to connect. This generated sketch still uses less than 100uA, both while waiting for a connection and while connected to your mobile and updating the menu/chart and logging the data.
With pfod_lp_nrf52_2024 the nRF52 is completely erased each time it is programmed. This removes the program protection and allows the softdevice and sketch to be uploaded.
If the softdevice flash works, then that is
OK, but often modules will have been protected against re-programming
and you will get this error output in the Arduino window
Open On-Chip Debugger 0.10.0-dev-00254-g696fc0a (2016-04-10-10:13)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
debug_level: 2
Info : only one transport option; autoselect 'swd'
adapter speed: 10000 kHz
cortex_m reset_config sysresetreq
Info : CMSIS-DAP: SWD Supported
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : CMSIS-DAP: FW Version = 1.10
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : reduce speed request: 10000kHz to 5000kHz maximum
Info : clock speed 10000 kHz
Info : SWD IDCODE 0x2ba01477
Error: Could not find MEM-AP to control the core
Error: Target not examined yet
Error while flashing SoftDevice.
From Nordic Semi – Debug and Trace page
CTRL-AP
- Control Access Port. The Control Access Port (CTRL-AP) is a custom
access port that enables control of the device even if the other
access ports in the DAP are being disabled by the access port
protection. Access port protection blocks the debugger from read and
write access to all CPU registers and memory-mapped addresses.
Disable access port protection. Access port protection can only
be disabled by issuing an ERASEALL command via CTRL-AP. This command
will erase the Flash, UICR, and RAM.
In that case you need to set the ERASEALL
command register in the nRF52 to clear the memory and make the device
programmable again. The version of openOCD supplied with
sandeepmistry nRF52 does not include the apreg command needed
to write to the ERASEALL command register so you need to install a
later version.
These
OpenOCD versions worked on Window
7https://github.com/ilg-archived/openocd/releases/tag/gae-0.10.0-20170124
has windows/mac and linux compiled versions.
The windows 64bit
version
https://github.com/ilg-archived/openocd/releases/download/gae-0.10.0-20170124/gnuarmeclipse-openocd-win64-0.10.0-201701241841-setup.exe
worked.
As did
https://github.com/ilg-archived/openocd/releases/download/v0.10.0-12-20190422/gnu-mcu-eclipse-openocd-0.10.0-12-20190422-2015-win64.zip
(These Windows pre-compiled
versions DID NOT WORK on Windows 7,
http://gnutoolchains.com/arm-eabi/openocd/
)
Open a command prompt as Administrator
and change dir to the OpenOCD install directory and enter the
command
(To open cmd as Administrator, type cmd into the windows
program search (bottom left) and then right click on cmd.exd listing
and choose run as Administrator.)
bin\openocd.exe
-d2 -f interface/cmsis-dap.cfg -f target/nrf52.cfg
You
may need to allow access to your local network
The response is
Open On-Chip Debugger 0.10.0 (2018-11-30) [https://github.com/sysprogs/openocd]
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
debug_level: 2
Info : auto-selecting first available session transport "swd". To override use '
transport select <transport>'.
adapter speed: 1000 kHz
cortex_m reset_config sysresetreq
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : CMSIS-DAP: SWD Supported
Info : CMSIS-DAP: FW Version = 1.10
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : clock speed 1000 kHz
Info : SWD DPIDR 0x2ba01477
Error: Could not find MEM-AP to control the core
Info : Listening on port 3333 for gdb connections
Then open a terminal window e.g. TeraTerm
(Windows) or CoolTerm
(Mac) and connect to 127.0.0.1 port 4444
The telnet window will show a > and the
command prompt will show
Info : accepting 'telnet' connection
on tcp/4444
In the telnet window (i.e. TeraTerm)
type
nrf52.dap apreg 1
0x04
this returns 0x00000000 showing the chip is protected.
Then type
nrf52.dap apreg 1
0x04 0x01
and then
nrf52.dap
apreg 1 0x04
this returns 0x00000001 showing the chip is
now set to ERASEALL on next restart.
Close the telnet connection and also use
Ctrl-C to exit the openOCD program in the command prompt and then
power cycle the nRF52 module and it will be now ready to program.
Now retry flashing the softdevice.
This tutorial has shown how you can easily create very low power BLE sketches in Arduino for the nRF52832 chip.
Supply currents of less than 20uA are easily achievable while keeping the chip active to make connections and send/receive data.
The free pfodDesigner lets you design menus/sub-menus, plot and log data and then generate the low power Arduino sketch for you. Connecting with pfodApp displays the menus and data while the nRF52 chip uses <20uA while not connected
No Android programming is required.
pfodApp handles all of that.
No Arduino
coding is required.
The free pfodDesignerV2 generates complete low power sketches.
The next part in this series will be A Very Low Power Temperature Monitor (under construction)
In most case the general Nordic BLE UART service is all that you will need to collect data and control your device. However you can use the underlying BLEPeripheral class to define your own services and characteristics, but you will also need to code an Andriod / iOS app to recognize it. If you want to use one of the 40 or more 'standard' BLE services , for which apps are already available, you will need to code that service and characteristics and format the data, in your sketch. Covering these services is beyond the scope of this tutorial.
AndroidTM is a trademark of Google Inc. For use of the Arduino name see http://arduino.cc/en/Main/FAQ
The General Purpose Android/Arduino Control App.
pfodDevice™ and pfodApp™ are trade marks of Forward Computing and Control Pty. Ltd.
Contact Forward Computing and Control by
©Copyright 1996-2024 Forward Computing and Control Pty. Ltd.
ACN 003 669 994