Getting started with Sensor SW 2.0
These instructions help you to get started with Movesense sensor software development (v. 2.0 and later). Do read this carefully since the toolchains have been completely changed from earlier versions!
This section is split into four parts:
- What's new in 2.0
- Toolchain and instructions for compiling the Movesense sensor software
- How to update the sensor over Bluetooth
- How to flash the sensor with the programming jig
- Desciption of the core software components of Movesense embedded platform
- Simple example project to get your first Movesense sensor software built
What's new in 2.0
- Completely re-vamped, docker based, build system for hardware compilations
- Works equally well in Windows, OSX and Linux
- Bluetooth 5.0 support:
- Faster transmission speed (1Mbps -> 2Mbps)
- Much improved bootloader
- Much faster firmware update speeds (1-2kB/s -> 10 kB/s)
- timeout in DFU mode (2 minutes) that boots back to application if there is no connection
- blink indication for data transfer
- Much improved BLE service modules:
- CustomGattService now supports full 128bit uuid's and more of them
- HRS (Standard HR Service) and NUS (Nordic UART Service) modules have now a simple Whiteboard API
- No need to create your own advertising packet with HRS service, service id added automatically
- Improved system settings
- More robust operation
- Improved DataLogger/Logbook storage control
- Prevent code gen of unneeded paths
- Storage format change with math conversion (Fixed point formats etc.)
- Ability to choose array lengths
- ECG logging support
- Hardware Watchdog
- Reboots a sensor that is in deadlock state for more than 30 seconds
- expanded Bluetooth memory to have more space for base-uuid's and user services (simultaneous NUS & HRS etc.)
- Changed Whiteboard BLE GATT communication to use new registered AmerSports 16-bit UUID
- Saves a lot of space from Advertising packet and RAM from Bluetooth stack
- requires the MDS 1.39 or newer
- TONS of bugfixes
Toolchain and usage
Software requirements
The following tools need to be installed in order to run the examples in this document.
Mandatory
- Docker Desktop for Windows or Mac
- or Install docker for Linux using your distributions package manager
Optional
- nRF5x-Command-Line-Tools-Win32 (needed with Movesense JIG)
- Visual Studio 2017 (Needed for simulator builds)
Movesense device library is available here.
Build commands (Real HW)
First pull the Movesense build environment container. The tag of the image should be the major version of the movesense-device-lib.
Use
docker pull movesense/sensor-build-env:2.1
for building e.g. version 2.1.5, and
docker pull movesense/sensor-build-env:2.2
for building firmware version 2.2.0.
Note: the tag "latest" is left for compatibility with earlier versions of this documentation and can be used for building versions 2.0.x and 2.1.x
To successfully build the Movesense software it's required to clone the Movesense-device-lib repository:
git clone git@bitbucket.org:movesense/movesense-device-lib.git
After that go to the cloned repository folder
cd movesense-device-lib
start the docker image on terminal:
Linux or Mac:
docker run -it --rm -v `pwd`:/movesense:delegated movesense/sensor-build-env:2.2
Windows:
docker run -it --rm -v c:/My/Project/Folder/movesense-device-lib:/movesense:delegated movesense/sensor-build-env:2.2
and when docker prompt starts:
cd /movesense
mkdir myBuild
cd myBuild
Now, run the CMake (needs to be done only once unless you add files to the project). It's possible to build both the debug and release version. In both cases the command will contain the following:
cmake -G Ninja -DMOVESENSE_CORE_LIBRARY=../MovesenseCoreLib/ -DCMAKE_TOOLCHAIN_FILE=../MovesenseCoreLib/toolchain/gcc-nrf52.cmake <sample_directory>
But to build a release version it's neccessary to specify the CMAKE_BUILD_TYPE option with the Release value:
cmake -G Ninja -DMOVESENSE_CORE_LIBRARY=../MovesenseCoreLib/ -DCMAKE_TOOLCHAIN_FILE=../MovesenseCoreLib/toolchain/gcc-nrf52.cmake -DCMAKE_BUILD_TYPE=Release <sample_directory>
Note that the
After that only the last step remains:
ninja pkgs
The created Movesense_combined.hex and the DFU packages can be found in the same directory.
Build commands (Sensor Simulator)
Starting on Movesense device library v1.7.0 it is possible to compile and run your sensor software in the Sensor Simulator. For more info, see the Sensor Simulator -document. To build the Movesense software for Sensor Simulator, clone the repository like above and create a build directory:
cd movesense-device-lib
mkdir simuBuild
cd simuBuild
Now create the Visual Studio 2019 solution using the CMake:
cmake -G "Visual Studio 16 2019" -A Win32 -DMOVESENSE_CORE_LIBRARY=../MovesenseCoreLib/ <sample_directory>
To build a release version, specify the CMAKE_BUILD_TYPE option with the Release value:
cmake -G "Visual Studio 16 2019" -A Win32 -DMOVESENSE_CORE_LIBRARY=../MovesenseCoreLib/ -DCMAKE_BUILD_TYPE=Release <sample_directory>
Note that the
The cmake will generate the Visual Studio solution-file (Project.sln) in the build directory. Just open it with the Visual Studio IDE, select "Movesense" project as a "Startup project" and "Debug" it.
How to update the sensor over Bluetooth
Because the Bluetooth stack (Softdevice) and bootloader have been updated, the first update of a sensor from 1.9.x (or older) to 2.0 has to be done with a DFU packet that contains a bootloader. These are recognizable by the filename that ends with _w_bootloader.zip. After the 2.0 bootloader has been installed, the application can be changed with the smaller "non-bootloader" DFU packet.
Also: The new SoftDevice requires the use of the latest nrf DFU library for Android / iOS. The updated Showcase apps (Android: 1.9.8, iOS: 1.0.5) are available in the usual places (bitbucket Downloads or Apple store).
NOTE: It is not possible to downgrade the sensor back to 1.9 series except with the programming jig!
How to flash the sensor with the programming jig
The updated bootloader has changed the way that the sensor is flashed, so the old piecemal flashing (bootloader, softdevice, application) will not work. Instead the hex files are merged in a single file "Movesense_combined.hex".
In Windows and Mac the flashing can be performed from the separate (non-docker) terminal window by running the following command from the shared build folder:
nrfjprog -f nrf52 --sectoranduicrerase --program Movesense_combined.hex --reset
NOTE: The old Movesense.hex and Movesense_w_settings.hex -files are no longer needed or used. The build process still leaves them around, but you should not use them for anything.
The flashing command ninja flash
, only works if the programming jig is attached to the computer that the command is run. In the new build system that "computer" is the virtual computer that runs the docker image. Attaching USB port to docker VM is possible in Linux but not in Windows or Mac. For instructions on how to attach USB to docker VM in linux, see here.
Description of main components
-
App.cpp
Main file of the application. -
app_root.yaml
File with configuration and list of the services. -
CMakeLists.txt
CMake building script. -
wbresources/
Directory for storing own yaml files. -
movesense-device-lib/
Movesense library.
App.cpp
#include "movesense.h"
Movesense.h contains necessary macros and definitions.
MOVESENSE_APPLICATION_STACKSIZE(1024)
You can define the application stack size. If you do not know how it works, do not change it.
MOVESENSE_PROVIDERS_BEGIN(n)
MOVESENSE_PROVIDER_DEF(xxxxx)
MOVESENSE_PROVIDER_DEF(xxxxx)
MOVESENSE_PROVIDER_DEF(xxxxx)
MOVESENSE_PROVIDERS_END(n)
MOVESENSE_PROVIDERS_BEGIN and MOVESENSE_PROVIDERS_END contain the list of the LoadableModule inherited classes (either Providers or Clients) that the App contains. The number given in BEGIN & END macros must be the exact number of MOVESENSE_PROVIDER_DEF-entries,
See "Creating own data provider" for more informations.
MOVESENSE_FEATURES_BEGIN()
Optional modules
The Movesense library has possibility to enable or disable some framework features, which will help you save some extra memory in your application by dropping the features that you do not need.
// Explicitly enable or disable Movesense framework core modules.
// List of modules and their default state is found in documentation
OPTIONAL_CORE_MODULE(DataLogger, true/false)
OPTIONAL_CORE_MODULE(Logbook, true/false)
OPTIONAL_CORE_MODULE(LedService, true/false)
OPTIONAL_CORE_MODULE(IndicationService, true/false)
OPTIONAL_CORE_MODULE(BleService, true/false)
OPTIONAL_CORE_MODULE(EepromService, true/false)
OPTIONAL_CORE_MODULE(BypassService, true/false)
OPTIONAL_CORE_MODULE(SystemMemoryService, true/false)
OPTIONAL_CORE_MODULE(DebugService, true/false)
OPTIONAL_CORE_MODULE(BleStandardHRS, true/false)
OPTIONAL_CORE_MODULE(BleNordicUART, true/false)
OPTIONAL_CORE_MODULE(CustomGattService, true/false)
EEPROM memory area definitions
If you use DataLogger/Logbook, direct EEPROM access or EEPROM storage for debug messages, you can define the memory areas for each with the following macros. Normal Movesense sensor has 384*1024 bytes of EEPROM, which can be accessed as one linear block. If you want to use some of it for your own app storage, just set the "offset" in macros below, to address different than 0.
LOGBOOK_EEPROM_MEMORY_AREA(offset, size);
Defines the are that the Logbook uses. offset and size must be multiple of 256. You can use size = MEMORY_SIZE_FILL_REST to use the rest of the memory for Logbook.
DEBUGSERVICE_BUFFER_SIZE(lines, characters);
DEBUG_EEPROM_MEMORY_AREA(enable, offset, size)
Defines the RAM & EEPROM area reserved for the Debug messages. set "enabled" to "true" to enable. For EEPROM storage, the offset and size must be multiples of 256. "DebugService" module must be enabled for this to work.
APPINFO_NAME("Sample Plain");
APPINFO_VERSION("1.0.0");
APPINFO_COMPANY("Movesense");
This is a portion where you should defined your application specific information. The content of these macros are returned in the GET /Info/App request.
NOTE: BLE_COMMUNICATION macro has been deprecated and should no longer be used.
The 2.2 release introduces the possibility to modify the Bluetooth configuration of the application as well as the memory allocated for Bluetooth stack (Softdevice) and Movesense HEAP.
Bluetooth configuration can be adjusted by the macros:
MOVESENSE_BLE_CONFIG_2PERIPHERALS
(the device supports two simultaneous peripheral roles)
or
MOVESENSE_BLE_CONFIG_1PERIPHERAL_1CENTRAL
(the device supports one peripheral role and one simultaneous central role)
or
MOVESENSE_BLE_CONFIG(bleConfig)
You provide your own BLEConfig struct which provides all the BLE adjustable configuration parameters. The BLEConfig struct contains following fields:
- BLE_GAP_DATA_LENGTH: Length of BLE packet (max MTU +4)
- BLE_PERIPHERAL_LINK_COUNT: Max count of peripheral links
- BLE_CENTRAL_LINK_COUNT: Max count of central links
- BLE_TOTAL_LINK_COUNT: How many links are there totally
- BLE_GAP_EVENT_LENGTH: How long is the BLE transmission length (in 1.25ms units)
- BLE_GATT_MAX_MTU_SIZE: Max MTU (use multiples of 23). This affects greatly to the memory need of the BLE stack (softdevice)
- BLE_GATTS_ATTR_TAB_SIZE: Size of BLE stack attribute table
- BLE_VS_UUID_COUNT: How many 128bit UUIDs the stack should have space for
If you create your own BLE configuration, it may be necessary to modify the BLE and HEAP RAM-allocations. See: CMakeLists.txt
MOVESENSE_FEATURES_END()
app_root.yaml
This yaml file contains configuration for the Whiteboard which is a part of the Movesense. You should not change the values if you do not understand them - it can change the stability of the platform.
# Type of the document
wbres:
version: "2.0"
type: "root"
# Execution context definitions
executionContexts:
application:
numberOfDpcs: 8
numberOfRequests: 20
numberOfResponses: 25
externalThread: true # we run this execution context in main thread
stackSize: 768
priority: normal
meas:
numberOfDpcs: 9
numberOfRequests: 25
numberOfResponses: 10
stackSize: 768
priority: normal
CMakeLists.txt
This file describes how the firmware is built. For the most part the Movesense build system is smart enough that you can just copy the file from another sample project. However, if you use 3rd party modules or custom BLE configuration you'll need to do some changes:
There are two variables that can be set that affect the RAM sizes:
# Override HEAP size
set(MOVESENSE_HEAP_SIZE 0xA000)
# Override app RAM allocation (see appflash.ld)
set(MOVESENSE_APP_RAM_SIZE 0xD000)
MOVESENSE_APP_RAM_SIZE specifies the size of RAM is used for application (as opposed to the BLE stack).
HINT: Study carefully the effect of these settings to the target.map and movesense.ld -files when you build your firmware.
Example Project
Suggested way of working with our library is not mandatory, but it helps to setup environment and to create first project.
Setting up the project
- Create git repository:
$ mkdir myproject
$ cd myproject
$ git init
- Add submodule:
$ git submodule add git@bitbucket.org:movesense/movesense-device-lib.git movesense-device-lib --depth 1 --force
- Copy plain application sample to main directory:
$ cp -R movesense-device-lib/samples/plain_app/* ./
Or optionally on mingw:
$ cp movesense-device-lib/samples/plain_app/* ./ -R
- Compile
- create and enter build dir:
$ mkdir builddir
$ cd builddir
- generate ninja files:
$ cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=../movesense-device-lib/MovesenseCoreLib/toolchain/gcc-nrf52.cmake -DMOVESENSE_CORE_LIBRARY=../movesense-device-lib/MovesenseCoreLib ../ -DCMAKE_BUILD_TYPE=Debug ../
-
run ninja to build DFU and hex files:
$ ninja
-
Test
Runninja dfupkg
and use the resulting zip file to update the firmware on movesense sensor with your phone (DFU).
Alternatively, runninja pkgs
to create both DFU file as well as hex files to use when flashing the sensor using the Jig. -
Great job - your project is now ready for development!
Creating own data provider
It is good practise to calculate on the Movesense to avoid additional BLE communication. So if you want to do some application which ex. is counting steps you should put the algorithm on the Movesense and send only result of the calculation to the mobile phone.
Creating API yaml file
The first step is to create the API yaml file. You can see the sample apps and the Movesense API.
API below contains only one path and only GET function which will be returning always value.
Test.yaml
swagger: '2.0'
info:
version: "0.0.0"
title: Test API
description: |
Description
termsOfService: http://xxxxx.com/
contact:
name: ABCD Company
url: http://COMPANYABCD.com
# Paths
paths:
/Sample/Test:
get:
description: |
Test function returning always 1
responses:
200:
schema:
description: New value
$ref: '#/definitions/Test'
definitions:
Test:
required:
- Value
properties:
Value:
description: Some description
type: integer
format: uint8
Changing app_root.yaml
apis:
Test.*:
apiId: 100
defaultExecutionContext: meas
Simple TestService Implementation
TestService.hpp
#pragma once
#include "app-resources/resources.h"
#include <whiteboard/LaunchableModule.h>
#include <whiteboard/ResourceProvider.h>
class TestService FINAL : private whiteboard::ResourceProvider,
public whiteboard::LaunchableModule
Service class should extend whiteboard::ResourceProvider and whiteboard::LaunchableModule classes.
{
public:
static const char* const LAUNCHABLE_NAME;
TestService();
~TestService();
private:
virtual bool initModule() OVERRIDE;
virtual void deinitModule() OVERRIDE;
virtual bool startModule() OVERRIDE { mModuleState = WB_RES::ModuleStateValues::STARTED; return true;}
virtual void stopModule() OVERRIDE { mModuleState = WB_RES::ModuleStateValues::STOPPED; }
It is a good practise to OVERRIDE all state functions. You can check implementation of initModule and deinitModule in TestService.cpp file.
virtual void onGetRequest(const whiteboard::Request& request,
const whiteboard::ParameterList& parameters) OVERRIDE;
};
onGetRequest is called when you will request data from the Movesense ( Sample/Test GET from yaml file). You can check other functions like onPutRequest, onSubscribe, ... in the Whiteboard documentation.
TestService.cpp
#include "movesense.h"
#include "TestService.hpp"
#include "app-resources/resources.h"
const char* const TestService::LAUNCHABLE_NAME = "TestSvc";
static const whiteboard::LocalResourceId sProviderResources[] = {
WB_RES::LOCAL::SAMPLE_TEST::LID,
};
TestService::TestService()
: ResourceProvider(WBDEBUG_NAME(__FUNCTION__), WB_RES::LOCAL::SAMPLE_TEST::EXECUTION_CONTEXT),
LaunchableModule(LAUNCHABLE_NAME, WB_RES::LOCAL::SAMPLE_TEST::EXECUTION_CONTEXT)
{}
TestService::~TestService()
{}
bool TestService::initModule()
{
if (registerProviderResources(sProviderResources) != whiteboard::HTTP_CODE_OK)
{
return false;
}
mModuleState = WB_RES::ModuleStateValues::INITIALIZED;
return true;
}
void TestService::deinitModule()
{
unregisterProviderResources(sProviderResources);
mModuleState = WB_RES::ModuleStateValues::UNINITIALIZED;
}
void TestService::onGetRequest(const whiteboard::Request& request,
const whiteboard::ParameterList& parameters)
{
if (mModuleState != WB_RES::ModuleStateValues::STARTED)
{
return returnResult(request, wb::HTTP_CODE_SERVICE_UNAVAILABLE);
}
switch (request.getResourceConstId())
{
case WB_RES::LOCAL::SAMPLE_TEST::ID:
{
WB_RES::Test test;
test.value = 1;
return returnResult(request, whiteboard::HTTP_CODE_OK, ResponseOptions::Empty, test);
}
break;
default:
return returnResult(request, whiteboard::HTTP_CODE_NOT_FOUND);
}
}
Update App.cpp
The last change necessary to complete our simple app.
#include "TestService.hpp"
MOVESENSE_PROVIDERS_BEGIN(1)
MOVESENSE_PROVIDER_DEF(TestService)
MOVESENSE_PROVIDERS_END(1)
MOVESENSE_PROVIDERS_BEGIN and MOVESENSE_PROVIDERS_END should be incremented and the new service added.