API reference
Introduction
Before creating your first application make sure to review provided samples under the samples directory. Instruction on how to compile them can be found in Readme.md file. Also check Getting Started section to know how to use the API. Each yaml file also contains a short description of the corresponding API.
General description
The API of each Movesense module, provider and application is presented in the form of a YAML file with the following structure:
swagger:
info:
version:
title:
description:
termsOfService:
contact:
paths:
definitions:
Each field contains the data regarding the described module. In this section the time API will be used as an example.
- Info - contains general information about the API that this module/provider/application provides.
version: NA
title: Time - Movesense-API
description: |
API for setting and reading device EPOCH time.
x-api-state: experimental
x-api-required: true
x-api-type: public
- Paths - each path listed in this field can be called using the wbcmd tool. This section also defines the operations available for each resource and path, most commonly PUT, GET, POST and DELETE. In general the structure of this section is as follows:
path:
operation:
description of the operation
parameters of the operation
response for the operation
operation:
...
So looking at part of the time.yaml example file the structure is:
/Time:
get:
description: |
Gets current time in number of microseconds since epoch 1.1.1970 (UTC).
If not explicitly set, contains time 2015-01-01 0:00 or earlier depending on the implementation.
responses:
200:
description: Operation completed successfully
schema:
type: integer
format: int64
put:
description: |
Sets current time in number of microseconds since epoch 1.1.1970 (UTC).
parameters:
- name: value
in: query
description: New time value.
required: true
type: integer
format: int64
responses:
200:
description: Operation completed successfully
/Time/Subscription:
post:
description: |
Subscription to the device time. Notifications are given every second.
responses:
200:
description: Operation completed successfully
x-notification:
description: |
Current time in microseconds since epoch 1.1.1970 (UTC).
schema:
type: integer
format: int64
delete:
description: |
Unsubscribes an active subscription.
responses:
200:
description: Operation completed successfully
- Definitions - each data or resource included in the path can be found here.
RelativeTime:
description: |
Relative time is time in milliseconds. It will start counting always from
zero after program startup / reset.
type: integer
format: uint32
x-unit: millisecond
Exchanging data with the sensor
Data stored on the sensor can be transferred and manipulated using the following operations:
-
PUT - sends data to the sensor overwriting previous data. The response will contain the status of the operation.
-
POST - sends new data record to sensor. The response will contain the status of the operation.
-
DELETE - sends a request to remove data. The response will contain the status of the operation.
-
GET - sends a request for data. The response will contain the requested data specified in the content part.
PUT operation requires additional parameters when called using the wbcmd tool:
-
opdatatype - type of data to be sent. Usually it's specified as type: or as a name of an object.
-
opdata - the actual data to be send to the device
If you do not have JIG for the serial communication, check "Exchanging data without programming jig" section below.
Typical PUT operation looks like this:
$ wbcmd --port COM4 --path /Time --op PUT --opdatatype int64 --opdata 2420070902199001
Note that the type of data specified by the opdatatype option is taken from the format label of the parameter description. Finally the data is given as the number of microseconds since exact epoch (see the description of PUT operation in the /Time path of the example).
Now if the specified time given in the opdata option is correct and has been applied successfully, the response will contain the HTTP_CODE_OK response string and 200 as its value:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/ECKI6AF7F95A/Time",
"content": "",
"querytimems": 28,
"querytimens": 28571575
}
GET operation is much simpler than PUT and requires only the op parameter to specify the operation type as GET. So the full command to get current time from the device will be:
$ wbcmd --port COM4 --path /Time --op GET
If the operation succeeds, requested data will be returned in the following response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/ECKI6AF7F95A/Time",
"content": 2420071515220000,
"querytimems": 24,
"querytimens": 24120770
}
For more details please refer to the wbcmd.
SUBSCRIBE operation allows to read data continuously. The following command will subscribe to time update notifications:
$ wbcmd --port COM4 --path /Time --op SUBSCRIBE
Response to the command above will be like this:
/net/ECKIA508508D/Time::onSubscribeResult
Subscribed and listening for notifications. Press ESC to stop:
@751 { 1420070420000000 }
@1759 { 1420070421000000 }
@2767 { 1420070422000000 }
@3774 { 1420070423000000 }
@4782 { 1420070424000000 }
@5790 { 1420070425000000 }
@6797 { 1420070426000000 }
/net/ECKIA508508D/Time::onUnsubscribeResult: SUCCESS{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "subscribe",
"querytimems": 7049,
"querytimens": 7049170573
}
Exchanging data without programming jig
WBCMD is a serial communication tool which also blocks the COM port so it's impossible to subscribe to multiple resources in parallel. Alternative solution is to use an Android sample application which contains the "AdbBridge" for Movesense. The sample app binary can be found here.
How to use:
- Install ADB and drivers for the Android phone
- Install sample application
ex.$ adb install sampleapp-debug-1.8.0.apk
- Open application and connect to Movesense sensor
- Open adb logcat to see output
$ adb logcat
Or if you use Gnu/Linux or Mingw to filter logs with data
$ adb logcat | grep OUTPUT
- Send command
ex.$ adb shell am broadcast -a android.intent.action.MOVESENSE --es type put --es path Component/Led --es value '''{\"isOn\":true}'''
More information can be found here under AdbBridge section.
Available APIs
/Info
Link to up-to-date YAML specification
/Info : GET
This API provides information about used device and the platform which is running on it. It is a good place from which you should start. You can get knowledge about the hardware version, serial number, app name or modules state. Of course there are also a lot other informations.
WBCMD
$ wbcmd --port COM13 --path /Info --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/info",
"content": {
"manufacturerName": "Suunto",
"brandName": null,
"productName": "Movesense",
"variant": "Unknown",
"design": null,
"hwCompatibilityId": "C",
"hwConfig": "SS2",
"serial": "174730000410",
"pcbaSerial": "UNKNOWN",
"sw": "1.2.0",
"hw": "UNKNOWN",
"additionalVersionInfo": null,
"addressInfo": [
{
"name": "BLE",
"address": "F5-27-21-37-AA-FC"
},
{
"name": "DFU-BLE",
"address": "F5-27-21-37-AA-FD"
}
],
"apiLevel": "1"
},
"querytimems": 51,
"querytimens": 51990127
}
Internal access
#include <movesense_info/resources.h>
...
void ExampleClient::getInfo() {
asyncGet(WB_RES::LOCAL::INFO());
}
void ExampleClient::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::INFO::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
const WB_RES::DeviceInfo &deviceInfo = resultData.convertTo< WB_RES::DeviceInfo >();
// Data structure from MovesenseCoreLib/generated/movesense_info/resources.h
//WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> manufacturerName;
//WB_ALIGN(4) whiteboard::Optional< whiteboard::WrapperFor32BitPointer<const char> > brandName;
//WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> productName;
//WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> variant;
//WB_ALIGN(4) whiteboard::Optional< whiteboard::WrapperFor32BitPointer<const char> > design;
//WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> hwCompatibilityId;
//WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> serial;
//WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> pcbaSerial;
//WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> sw;
//WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> hw;
//WB_ALIGN(4) whiteboard::Optional< VersionInfoArray > additionalVersionInfo;
//WB_ALIGN(4) whiteboard::Optional< whiteboard::Array< AddressInfo > > addressInfo;
//WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> apiLevel;
}
break;
}
}
/Info/App
/Info/App : GET
This API provides information about the application running on your Movesense sensor.
WBCMD
$ wbcmd --port COM13 --path /Info/App --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Info/app",
"content": {
"name": "Sample Connection Scanner",
"version": "1.0.0",
"company": "Movesense",
"modules": {
"data": [
{
"name": "DataLogger",
"enabled": true
},
{
"name": "Logbook",
"enabled": true
},
{
"name": "LedService",
"enabled": true
},
{
"name": "IndicationService",
"enabled": true
},
{
"name": "BleService",
"enabled": true
},
{
"name": "EepromService",
"enabled": false
}
]
}
},
"querytimems": 49,
"querytimens": 49053506
}
Internal access:
#include <movesense_info/resources.h>
...
void ExampleClient::getAppInfo() {
asyncGet(WB_RES::LOCAL::INFO_APP());
}
void ExampleClient::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::INFO_APP::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
const WB_RES::AppInfo &appInfo = resultData.convertTo< WB_RES::AppInfo >();
// Data structure from MovesenseCoreLib/generated/movesense_info/resources.h
// WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> name;
// WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> version;
// WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> company;
// WB_ALIGN(4) whiteboard::Optional< ModulesStatusArray > modules;
// WB_ALIGN(4) whiteboard::Array< ModuleStatus > data;
// WB_ALIGN(4) whiteboard::WrapperFor32BitPointer<const char> name;
// WB_ALIGN(1) bool enabled;
}
break;
}
}
/Time
Link to up-to-date YAML specification
/Time : GET, PUT, SUBSCRIBE
This API provides access to the UTC time set on the sensor. The time is given as microseconds since the 0:00 1st Jan 1970 UTC. The subscribe gives time notification once a second.
WBCMD
$ wbcmd --port COM13 --path /Time --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/213030000182/Time",
"content": 1698303360939000,
"querytimems": 31,
"querytimens": 31490297
}
$ wbcmd --port COM13 --path /Time --op PUT --opdata 1698303354632843
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/213030000182/Time",
"querytimems": 79,
"querytimens": 79427290
}
/Time/Detailed
/Time/Detailed : GET, SUBSCRIBE
TimeDetailed -API provides deeper information about the time relationship between the sensor timestamps (milliseconds since the sensor reset) and UTC time (microseconds since the 0:00 1st Jan 1970 UTC). The subscription gives the UTC-timestamp mapping at the moment of subscription and every time a new UTC time is PUT to the sensor. This enables the storing of UTC time mapping on the DataLogger/Logbook storage together with other sensor data.
WBCMD
$ wbcmd --port COM13 --path /Time/Detailed --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/213030000182/Time/Detailed",
"content": {
"utcTime": 1698303649929000,
"relativeTime": 469038,
"tickRate": 1024,
"accuracy": 20
},
"querytimems": 245,
"querytimens": 245877669
}
$ wbcmd --port COM13 --path /Time/Detailed --op SUBSCRIBE
Response:
/net/213030000182/Time/Detailed::onSubscribeResult
Subscribed and listening for notifications. Press ESC to stop:
@320 { {"utcTime": 1698303924546000, "relativeTime": 2555} }
/Comm/Ble
Link to up-to-date YAML specification
/Comm/Ble/Addr : GET
/Comm/Ble/Adv : GET, POST, DELETE
/Comm/Ble/Adv/Settings : GET, PUT
/Comm/Ble/Peers : GET
/Comm/Ble/Peers/Subscription : POST, DELETE
/Comm/Ble/Peers/{ConnHandle} : DELETE
/Comm/Ble/Security/Bonds : GET, DELETE
/Comm/Ble/Security/Pin : PUT, DELETE
/Comm/Ble/Security/Settings : PUT, GET
This API allows you to manage and retrieve BLE related data
Reading information about your own BLE address
Like any BLE device, Movesense sensor has its own unique BLE MAC address. To obtain its value, read the key Comm/Ble/Addr
. The MAC address can not be changed
WBCMD
$ wbcmd --port COM3 --path Comm/Ble/Addr --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Addr",
"content": "C1:EA:DD:A5:C2:23",
"querytimems": 31,
"querytimens": 31793876
}
Connection management
Display list of connected devices. Currently Movensense sensor supports only BLE peripheral role with one connection.
$ wbcmd.exe --port COM3 --path Comm/Ble/Peers --op get
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Peers",
"content": {
"ConnectedPeers": [
{
"Address": "78:35:F8:42:82:6B",
"Name": null,
"handle": 0
}
]
},
"querytimems": 41,
"querytimens": 41989589
}
Disconnect selected device:
$ wbcmd.exe --port COM3 --path Comm/Ble/Peers/0 --op delete
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "delete",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Peers/0",
"content": "",
"querytimems": 32,
"querytimens": 32003054
}
Subscription for connection notification
$ wbcmd.exe --port COM3 --path Comm/Ble/Peers --op subscribe
/net/ECKI1876A0BD/Comm/Ble/Peers::onSubscribeResult
Subscribed and listening for notifications. Press ESC to stop:
@3307 { {
"Peer": {
"Address": "6B:9C:63:53:09:67",
"Name": null,
"handle": 0
},
"State": [
1,
"CONNECTED"
]
} }
@7813 { {
"Peer": {
"Address": "6B:9C:63:53:09:67",
"Name": null,
"handle": 0
},
"State": [
0,
"DISCONNECTED"
]
} }
Connetion parameters
The Ble connection parameters can be changed starting on version 2.2.
To read and modify connection parameters of an ongoing connection, use the GET & PUT requests on the resource Comm/Ble/Peers/[ConnHandle]/Params
.
To modify the preferred connection parameters, use PUT on resource Comm/Ble/ConnParams
.
Advertising control
Advertising is a process of broadcasting a device information in a BLE network that provides other devices with readiness to connect. If advertising is turned off, no device can connect to Movesense. Turning on, switching off and reading the broadcasting state is done by accessing the resource Comm/Ble/Adv
WBCMD
Reading advertising state - flag isAdvertising indicate if advertising is turn on. Field PeerAddr is depreciated.
Checking of advertising state:
$ wbcmd --port COM3 --path Comm/Ble/Adv
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Adv",
"content": {
"isAdvertising": true,
"PeerAddr": null
},
"querytimems": 31,
"querytimens": 31552227
}
Turning on advertising:
$ wbcmd --port COM3 --path Comm/Ble/Adv --op post
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "post",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Adv",
"content": "",
"querytimems": 31,
"querytimens": 31804071
}
Turning off advertising:
$ wbcmd --port COM3 --path Comm/Ble/Adv --op delete
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "delete",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Adv",
"content": "",
"querytimems": 31,
"querytimens": 31744791
}
Advertising settings
During advertising, the device periodically sends a packet containing various information about the device, such as the name of the device, list of services, etc. By requesting of remote peer, this list can be supplemented with additional information contained in the "Scan Response" packet. The contents of both packages has a default value set in code, however it can be changed. For Adverising and scan response data see Bluetooth Core Specification v 5.0 or see examples, e.g.: https://www.pubnub.com/blog/2015-04-14-building-android-beacon-android-ibeacon-tutorial-overview/
The advertising settings can also change the time between sending subsequent broadcast packets and the time after which the device will stop advertise. It should be remembered that more frequent sending of advertising packets makes establishing a connection easier, but it involves a bigger power consumption.
The default advertisement data contain the following data: * AD Flags, 1 byte (type 0x1) * 0x06 (LE General Discoverable Mode, BR/EDR Not Supported) * Manufacturer specific data, 6 bytes (type 0xFF) * {0x9F, 0x00} (Company ID Suunto) * {0x01, 0x02, 0x3, 0x00} * Complete List of 128-bit Service Class UUIDs (type 0x07) * Whiteboard UUID: { 0x41, 0x00, 0x74, 0x70, 0x63, 0x88, 0x7A, 0xB5, 0xCC, 0x49, 0x31, 0x82, 0x90, 0x30, 0x35, 0x61 }
The default scan response packet contains the following data: * Complete Local Name (type 0x09) * String containing device name and serial number: "Movesense 'serial number'"
WBCMD
Change settings - in this example only Advertising Packet is changed, Scan Response packet leave default. Advertising interval is set to 40 * 0.625 ms = 25 ms and never expires (Timeout = 0):
$ wbcmd.exe --port COM3 --path Comm/Ble/Adv/Settings --op put --opdatatype AdvSettings --opdata '{"AdvPacket": [2, 1, 6, 10, 9, 84, 101, 115, 116, 32, 78, 97, 109, 101], "Interval": 40, "Timeout": 0 }'
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Adv/Settings",
"content": "",
"querytimems": 31,
"querytimens": 31972470
}
Read settings:
$ wbcmd.exe --port COM3 --path Comm/Ble/Adv/Settings --op get
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Adv/Settings",
"content": {
"AdvPacket": null,
"ScanRespPacket": null,
"Interval": 40,
"Timeout": 0
},
"querytimems": 32,
"querytimens": 32027597
}
Bonding
Bonding is a process of exchanging security data and saving them for use during next connection. Smartphone user's point of view this operation may be also called pairing. Bonding / pairing is always initiated by the mobile application or through the Android / IOS system menu.
Because Movesense has no display or buttons, only one bonding mode is available as per specification: just works pairing, in which there is no additional pincode security. To increase the level of security, the device has a static passkey mode in which you can define a static 6-digit pincode. This mode is workaround for BLE specification which always requires randomly generated pincodes.
Unlike other devices, the Movesense can only be paired with one central device. After proper authorization, Movesense stores the cryptographic keys and the peer MAC address in the FLASH memory. Next bonding deletes previous authentication data.
WBCMD
Displaying list of bonded devices (currently maximum one device):
$ wbcmd.exe --port COM3 --path Comm/Ble/Security/Bonds --op get
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Security/Bonds",
"content": {
"BondedDevices": [
{
"Address": "40:88:05:C4:63:3A"
}
]
},
"querytimems": 31,
"querytimens": 31502009
}
Delete all bondings:
$ wbcmd.exe --port COM3 --path Comm/Ble/Security/Bonds --op delete
WbCmd Movesense:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "delete",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Security/Bonds",
"content": "",
"querytimems": 31,
"querytimens": 31704768
}
Bonding pincode
WBCMD
Setting a static pincode for pairing authorisation:
$ wbcmd.exe --port COM3 --path Comm/Ble/Security/Pin --op put --opdatatype string --opdata 123456
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Security/Pin",
"content": "",
"querytimems": 31,
"querytimens": 31565820
}
Delete the static pincode and enable just works pairing:
$ wbcmd.exe --port COM3 --path Comm/Ble/Security/Pin --op delete
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "delete",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Security/Pin",
"content": "",
"querytimems": 31,
"querytimens": 31545808
}
Bonding policy and recovery mode
Bonding policy is an extension of the standard BLE bonding settings. It allows to limit the possibility of pairing new devices to increase the security level.
- Bonding Enable (0) - always llow bonding with new device. New bondings delete previous.
- Bonding Disabled (1) - do not allow bond any devices other than those already bonded or the recovery mode is entered.
- Bonding Once (2) - allow bonding with only one device. After bonding, the next devices are blocked until the current bond is deleted or the recovery mode is entered.
- Bonding Same MAC (3) - like BondingOnce, but rebonding to device with same MAC address is allowed.
Note: when bonding policy is set to 3 the phone with invalid MAC always reports successful pairing, but Movesense is not paired. This may be very confusing to the user. This behavior results from the BLE specification - to get to know the real MAC address of the phone we have to pair. If as a result of pairing we find that another phone has been connected, new cryptographic keys are not stored and Movesense uses the previous ones. However, at the moment there is no possibility to remove the pairing on the phone side and phone or report an error.
WBCMD
Getting current settings:
$ wbcmd.exe --port COM3 --path Comm/Ble/Security/Settings --op get
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Security/Settings",
"content": {
"Policy": [
0,
"BondingEnabled"
],
"RecoveryTime": 0
},
"querytimems": 31,
"querytimens": 31720249
}
Setting new bonding policy and recovery time:
$ wbcmd.exe --port COM3 --path Comm/Ble/Security/Settings --op put --opdatatype BondingSettings --opdata '{"Policy": 1, "RecoveryTime": 30}'
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/ECKI1876A0BD/Comm/Ble/Security/Settings",
"content": "",
"querytimems": 31,
"querytimens": 31446505
}
Built-in BLE modules
The movesense comes with two built-in BLE services as optional modules (enabled in App.cpp): Standard Heart Rate Service (HRS) and Nordic Semiconductor UART Service (NUS).
BleStandardHRS
The API description of the BleStandardHRS-module describes two operations, POST and SUBSCRIBE, on single resource /Comm/Ble/HRS.
-
SUBSCRIBE: Provides notification when a client (e.g. sport watch) enables or disables the HRS service notification characteristic.
-
POST: Sends a new update of HR and RR data to the client.
NOTE: The BleStandardHRS-module modifies the default advertising packet to include the UUID of the HRS service automatically, so there is no need to modify the advertising packet.
BleNordicUART
The API description of the BleNordicUART-module describes two operations, POST and SUBSCRIBE, on single resource /Comm/Ble/NUS.
-
SUBSCRIBE: Provides notification when a client (e.g. sport watch) sends data to the Movesense sensor.
-
POST: Sends data to the client.
Sample applications
BLE related sample applications can be found here:
BLE STD services
Custom BLE advertising
Custom GATT services
GATT Sensordata
Bonding example
/System/Debug
Link to up-to-date YAML specification
/System/Debug/Config : GET, PUT
/System/Debug/{Level} : SUBSCRIBE
/System/Debug/Log : GET
/System/Debug/Log/Config : GET, PUT
Debug Service provides access to debug and diagnostics messages from the device. It is designed to be a replacement for DEBUGLOG
macros, with new features such as priority levels and remote access.
There are 5 priority levels of debug messages:
- fatal
- error
- warning
- info
- verbose
Subscribers of a given level will be notified about messages with the same or higher priority (e.g. with subscription to warning
, you will receive fatal
and error
messages as well, but info
and verbose
messages will be omitted).
Messages are also divided into 2 groups, depending on their source:
- user messages (application-side),
- system messages (from internal resource providers, hardware drivers, etc.).
Each of those groups can be enabled or disabled separately (see: configuration).
Configuration
Debug Service can be configured through path /System/Debug/Config
. Currently configuration allows to enable/disable messages by source.
WBCMD
Setting new configuration:
wbcmd --port COM13 --path System/Debug/Config --op PUT --opdatatype DebugMessageConfig --opdata '{"SystemMessages":true,"DebugMessages":true}'
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/174730000410/System/Debug/Config",
"content": "",
"querytimems": 31,
"querytimens": 31650644
}
Configuration can be retrieved by sending GET request on the same path.
INTERNAL ACCESS
#include <system_debug/resources.h>
...
void ExampleClient::setDebugConfig()
{
WB_RES::DebugMessageConfig config;
config.systemMessages = true;
config.userMessages = true;
asyncPut(WB_RES::LOCAL::SYSTEM_DEBUG_CONFIG,
AsyncRequestOptions::Empty,
config);
}
void ExampleClient::onPutResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::SYSTEM_DEBUG_CONFIG::LID:
{
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
Subscribing to debug messages
WBCMD
Replace path parameter Verbose
according to the desired priority level.
wbcmd --port COM13 --path System/Debug/Verbose --op SUBSCRIBE
Example notification:
@133 { {
"Timestamp": 1106,
"Level": [
3,
"Info"
],
"Tag": "",
"Message": "Hello world!"
} }
Sending messages
DebugService is an optional module; it can be enabled in App.cpp:
OPTIONAL_CORE_MODULE(DebugService, true)
Due to limitations related with multithreading, DebugService internally stores messages in a queue-like buffer before sending them. It can be configured in order to optimize memory consumption:
DEBUGSERVICE_BUFFER_SIZE(6, 120)
First parameter concerns maximum number of stored lines, second one is a total number of stored characters - e.g. configuration (6, 120)
allows to store two 60-character-long messages (incl. null-terminators), but the 3rd message will be clipped (replaced with '...'); or it allows to store 6 messages of average length 20 characters, but will indicate that 7th message did not fit.
DebugLogger.hpp
provides a convenient interface for sending debug messages from the application code. It defines class DebugLogger
containing static printf-like methods corresponding to debug levels.
Example usage:
DebugLogger::info("Hello world!");
DebugLogger::verbose("> i = %d", 42);
Tags have not been implemented yet - empty tag ""
is always displayed.
/System/Debug/Log
It is possible to store debug messages in EEPROM and retrieve them later, e.g. after the BLE connection is established.
This feature is disabled by default, to prevent data corruption caused by overwriting memory regions that are already in use. It can be enabled and configured in App.cpp
(note: DebugService
has to be enabled):
DEBUG_EEPROM_MEMORY_AREA(enabled, offset, size)
Parameters:
- enabled
- determines if this feature is enabled; can be either true
or false
,
- offset
- determines the beginning of the memory region used for message storage; must be a multiple of 256,
- size
- determines size of the memory region dedicated for message storage; must be at least 256 bytes and less than 65536 bytes.
Example App.cpp
configuration:
OPTIONAL_CORE_MODULE(DebugService, true)
DEBUGSERVICE_BUFFER_SIZE(6, 120)
DEBUG_EEPROM_MEMORY_AREA(true, 0, 2048)
Logger configuration
Aside from configuration in App.cpp
, log storage can be also configured by requests performed on resource path /System/Debug/Log/Config
. It currently allows filtering messages by priority level - to prevent low-priority messages from being stored and therefore to optimize performance.
WBCMD
Getting current configuration:
wbcmd --port COM13 --path System/Debug/Log/Config --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/System/Debug/Log/Config",
"content": {
"MinimalLevel": [
2,
"Warning"
]
},
"querytimems": 31,
"querytimens": 31975330
}
Setting new configuration:
wbcmd --port COM13 --path System/Debug/Log/Config --op PUT --opdatatype DebugLogConfig --opdata '{"MinimalLevel":2}'
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/174730000410/System/Debug/Log/Config",
"content": "",
"querytimems": 31,
"querytimens": 31478470
}
INTERNAL ACCESS
Setting new configuration:
#include <system_debug/resources.h>
...
void ExampleClient::setDebugLogConfig()
{
WB_RES::DebugLogConfig config;
config.minimalLevel = WB_RES::DebugLevel::WARNING;
asyncPut(WB_RES::LOCAL::SYSTEM_DEBUG_LOG_CONFIG,
AsyncRequestOptions::Empty,
config);
}
void ExampleClient::onPutResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::SYSTEM_DEBUG_LOG_CONFIG::LID:
{
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
Retrieving stored messages
Stored messages can be accessed using path /System/Debug/Log
. The log is ordered in the order by the Id. If the sensor experiences a reset situation the timestamp will restart from 0 but the Id will continue. The next log message will have an Id of LargestStoredId +1. Query parameters consist of Largest Id we are interrested and optionally largest timestamp and number of messages to be sent.
Messages are chosen by id up to the given value (equal or nearest lower), and up to the given amount and timestamp.
Number of returned messages may be smaller than requested, due to limitations of internal RAM buffering when reading messages from EEPROM.
If no messages match the criteria or if debug storage is disabled, error code 204 NO CONTENT
is returned.
WBCMD
Getting latest log messages:
wbcmd --port COM13 --path System/Debug/Log --op GET --opdatatype DebugLogQuery --opdata '{"Id":999999999}'
Example response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/system/debug/log",
"content": {
"Messages": [
{
"Id": 23,
"Timestamp": 199591,
"Level": [
3,
"Info"
],
"Tag": "",
"Message": "Hello World #7!"
},
{
"Id": 22,
"Timestamp": 198591,
"Level": [
3,
"Info"
],
"Tag": "",
"Message": "Hello World #6!"
},
{
"Id": 21,
"Timestamp": 197591,
"Level": [
3,
"Info"
],
"Tag": "",
"Message": "Hello World #5!"
}
]
},
"querytimems": 53,
"querytimens": 53906298
}
To get the next earlier messages, query again setting query object "Id" to value "Smallest returned Id -1" again and again until no entrieas are returned.
/System/Memory
Link to up-to-date YAML specification
/System/Memory/Heap : GET
Memory API allows to monitor heap usage. It provides information about memory usage and allows to set up a test that checks if the available heap memory is sufficient for further operations, i.e. it has never exceeded the required level.
Getting memory usage information
System memory service is an optional module; it has to be enabled in App.cpp
:
OPTIONAL_CORE_MODULE(SystemMemoryService, true)
WBCMD
wbcmd --port COM13 --path System/Memory/Heap --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/System/Memory/Heap",
"content": {
"Size": 45312,
"Free": 11448,
"Used": 33864,
"LowestFree": 11360,
"SufficiencyTestResult": [
0,
"NotTested"
]
},
"querytimems": 40,
"querytimens": 40847066
}
Internal access
#include <system_memory/resources.h>
...
void ExampleClient::getHeapStatus()
{
asyncGet(WB_RES::LOCAL::SYSTEM_MEMORY_HEAP());
}
void CustomService::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::SYSTEM_MEMORY_HEAP::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
auto memory = resultData.convertTo<const WB_RES::Memory&>();
// handle heap state
DebugLogger::info("Heap state GET result, free: %d", memory.free);
}
else
{
// handle error
}
break;
}
}
}
Setting the sufficiency test
Sufficiency test can be set up in order to validate that heap usage has not exceeded the minimum required level. It is usually best to trigger it right after the application's initialization/start-up phase.
#include "HeapSizeValidator.hpp"
...
HeapSizeValidator::checkHeapSizeIsSufficient(8000); // set the parameter accordingly to the requirements
Test result can be retrieved by making GET request. Field SufficiencyTestResult
should then be set to either 1
(passed) or 2
(failed).
/System/Mode
Link to up-to-date YAML specification
/System/Mode : GET, PUT
System Mode API allows changing the sensor to different modes. Not all modes listed in the API document are implemented in the Movesense sensor (the API is shared with other devices). The list of applicable modes are:
- FullPowerOff (=1): Turns the sensor off. The wakeup can be setup to HR-Stud contact or Movement.
- Application (=5): Sets sensor into application mode and runs the firmware application. This is the normal startup mode after power on if there is a valid application firmware on the sensor. If the sensor is already in application mode, resets the sensor.
- FwUpdateMode (=12): Resets the sensor and boots up in the Firmware update mode.
Note: The "PowerOff"-mode (=3) is not possible in the current Movesense sensor hardware. To achieve the low power state while keeping the real time clock, disable advertising, disconnect BLE connection and unsubscribe from all sensor data. Even though the CPU will stay powered on, the power consumption is low at ~10 uA (vs. 3-5 uA in FullPowerOff).
WBCMD
wbcmd --port COM13 --path System/Mode --op PUT --opdata 1
/System/States
Link to up-to-date YAML specification:
/System/States/{StateID} : GET, SUBSCRIBE
States API is a uniform, simplistic interface for accessing states of internal components.
Currently available states:
-
0: movement:
- 0: device not moving
- 1: device moving
-
1: battery status:
- 0: battery OK
- 1: battery low
-
2: connectors:
- 0: disconnected
- 1: connected to gear
- 2: unknown state
-
3: double-tap:
- 0: normal state
- 1: device was double-tapped
-
4: tap:
- 1: device was tapped
-
5: free-fall:
- 0: device is under acceleration
- 1: device is in free fall
Subscription to state change
Set path parameter {StateId}
according to desired state source (e.g. 0
for movement detection).
WBCMD
wbcmd --port COM13 --path System/States/0 --op SUBSCRIBE
Example notification:
@15 { {
"Timestamp": 61842,
"StateId": [
0,
"Movement"
],
"NewState": 0
} }
Internal access
#include <system_states/resources.h>
...
void ExampleClient::subscribeMovementDetection()
{
int32_t stateId = WB_RES::StateIdValues::Type::MOVEMENT;
asyncSubscribe(WB_RES::LOCAL::SYSTEM_STATES_STATEID(),
AsyncRequestOptions::Empty,
stateId);
}
void ExampleClient::onSubscribeResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::SYSTEM_STATES_STATEID::LID:
{
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
void ExampleClient::onNotify(whiteboard::ResourceId resourceId,
const whiteboard::Value& value,
const whiteboard::ParameterList& parameters)
{
switch (resourceId.getConstId())
{
case WB_RES::LOCAL::SYSTEM_STATES_STATEID::ID:
{
auto stateChange = value.convertTo<const WB_RES::StateChange&>();
// handle state change
DebugLogger::info("State changed, ID: %d, state: %d",
stateChange.stateId.getValue(),
stateChange.newState);
}
break;
}
}
Getting current state
GET operation can be performed to retrieve current state of a component. Note that this can be unimplemented for some state sources due to physical limitations, e.g. movement detection requires accelerometer enabled and running.
WBCMD
wbcmd --port COM13 --path System/States/1 --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/System/States/1",
"content": 0,
"querytimems": 31,
"querytimens": 31697092
}
Internal access
#include <system_states/resources.h>
...
void ExampleClient::getBatteryState()
{
asyncGet(WB_RES::LOCAL::SYSTEM_STATES_STATEID(),
AsyncRequestOptions::Empty,
WB_RES::StateIdValues::Type::BATTERYSTATUS);
}
void ExampleClient::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::SYSTEM_STATES_STATEID::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
auto state = resultData.convertTo<const WB_RES::State&>();
// handle state
DebugLogger::info("State GET result, state: %d", state);
}
else
{
// handle error
}
break;
}
}
}
Sample application
States service related sample application can be found here:
Connection scanner
/Meas/Acc
Link to up-to-date YAML specification
/Meas/Acc/Info : GET
/Meas/Acc/Config : GET, PUT
/Meas/Acc/{SampleRate} : SUBSCRIBE
Meas Acc API gives possibility to collect data from linear acceleration sensor.
Sensor capabilities
Linear acceleration sensor is capable to deliver a lot of useful data about motion, acceleration and shocks. The sensor data can be also used for position calculation by using AHRS algoritms. It should be configured for each use case separately. It handles different working frequencies and different acceleration sensitivities. For efficient work with accelerometer the power consumption and accelerometer frequency should be taken into consideration.
To get info about available frequencies and sensitivity ranges use Meas/Acc/Info path:
WBCMD
wbcmd --port COM13 --path Meas/Acc/Info --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Meas/Acc/Info",
"content": {
"SampleRates": [
13,
26,
52,
104,
208,
416,
833,
1666
],
"Ranges": [
2,
4,
8,
16
]
},
"querytimems": 40,
"querytimens": 40911264
}
Getting configuration
There is a possibility to check actual sensitivity configuration of the accelermoter. Please refer to example below:
WBCMD
wbcmd --port COM13 --path Meas/Acc/Config --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Meas/Acc/Config",
"content": {
"GRange": 8
},
"querytimems": 31,
"querytimens": 31733842
}
Changing configuration
Next example will show how to change the sensitivity range. It is possiblie to change it during an active subscription on the accelerometer data. Please notice that changing sensitivity during accelerometer run can change behavior of some accelerometer features, like in example event detection (API - system/states)
WBCMD
wbcmd --port COM13 --path Meas/Acc/Config --op PUT --opdatatype AccConfig --opdata '{"GRange":4}'
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/174730000410/Meas/Acc/Config",
"content": "",
"querytimems": 47,
"querytimens": 47791840
}
Internal access
#include <meas_acc/resources.h>
...
void ExampleClient::setAccelerometerConfig()
{
WB_RES::AccConfig accConfig;
accConfig.gRange = 4;
asyncPut(WB_RES::LOCAL::MEAS_ACC_CONFIG(),
AsyncRequestOptions::Empty,
accConfig);
}
void ExampleClient::onPutResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::MEAS_ACC_CONFIG::LID:
{
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
Capturing accelerometer data
There are different ways of capturing data from accelerometer. Please note that using highest sample rates may be limited by the BLE bandwidth. Good practise is to collect raw data internally, process it and send only calculated result to the remote client.
WBCMD
wbcmd --port COM13 --path Meas/Acc/13 --op SUBSCRIBE
Example notification:
@384 { {
"Timestamp": 3015035,
"ArrayAcc": [
{
"x": -0.03230426087975502,
"y": -0.43790218234062195,
"z": 9.9413366317749023
}
]
} }
Internal access
#include <meas_acc/resources.h>
...
void ExampleClient::subscribeAcc()
{
int32_t sampleRate = 13;
asyncSubscribe(WB_RES::LOCAL::MEAS_ACC_SAMPLERATE(),
AsyncRequestOptions::Empty,
sampleRate);
}
void ExampleClient::onSubscribeResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::MEAS_ACC_SAMPLERATE::LID:
{
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
void ExampleClient::onNotify(whiteboard::ResourceId resourceId,
const whiteboard::Value& value,
const whiteboard::ParameterList& parameters)
{
switch (resourceId.getConstId())
{
case WB_RES::LOCAL::MEAS_ACC_SAMPLERATE::ID:
{
auto accData = value.convertTo<const WB_RES::AccData&>();
// handle received data
DebugLogger::info("Acc data, timestamp: %d", accData.timestamp);
}
break;
}
}
Sample application
Sample application using Meas/Acc API can be found here:
Jumpmeter sample application
Accelerometer application
Compressed acceleration application
/Meas/Gyro
Link to up-to-date YAML specification
/Meas/Gyro/Info : GET
/Meas/Gyro/Config : GET, PUT
/Meas/Gyro/{SampleRate} : SUBSCRIBE
Meas Gyro API gives possibility to collect data from Gyroscope sensor.
Sensor capabilities
Gyroscope sensor is capable to deliver a lot of useful data for measuring or maintaining orientation and angular velocity. It should be configured for each use case separately. It handles different working frequencies and different velocity sensitivities. For efficient work with gyroscope the power consumption and gyroscope frequency should be taken into consideration.
To get info about available frequencies and sensitivity ranges use Meas/Gyro/Info path:
WBCMD
wbcmd --port COM13 --path Meas/Gyro/Info --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Meas/Gyro/Info",
"content": {
"SampleRates": [
13,
26,
52,
104,
208,
416,
833,
1666
],
"Ranges": [
245,
500,
1000,
2000
]
},
"querytimems": 40,
"querytimens": 40911264
}
Getting configuration
There is a possibility to check actual sensitivity configuration of the accelermoter. Please refer to example below:
WBCMD
wbcmd --port COM13 --path Meas/Gyro/Config --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Meas/Gyro/Config",
"content": {
"DPSRange": 2000
},
"querytimems": 31,
"querytimens": 31733842
}
Changing configuration
Next example will show how to change the sensitivity range. It is possiblie to change it during an active subscription on the gyroscope data.
WBCMD
wbcmd --port COM13 --path Meas/Gyro/Config --op PUT --opdatatype GyroConfig --opdata '{"DPSRange":1000}'
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/174730000410/Meas/Gyro/Config",
"querytimems": 47,
"querytimens": 47791840
}
Internal access
#include <meas_gyro/resources.h>
...
void ExampleClient::setGyroscopeConfig()
{
WB_RES::GyroConfig gyroConfig;
gyroConfig.DPSRange = 1000;
asyncPut(WB_RES::LOCAL::MEAS_GYRO_CONFIG(),
AsyncRequestOptions::Empty,
gyroConfig);
}
void ExampleClient::onPutResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::MEAS_GYRO_CONFIG::LID:
{
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
Capturing gyroscope data
There are different ways of capturing data from gyroscope. Please note that using highest sample rates may be limited by the BLE bandwidth. Good practise is to collect raw data internally, process it and send only calculated result to the remote client.
WBCMD
wbcmd --port COM13 --path Meas/Gyro/52 --op SUBSCRIBE
Example notification:
@955 { {
"Timestamp": 108412,
"ArrayGyro": [
{
"x": 0.56000000238418579,
"y": -2.5199999809265137,
"z": -0.84000003337860107
}
]
} }
Internal access
#include <meas_gyro/resources.h>
...
void ExampleClient::subscribeGyro()
{
int32_t sampleRate = 52;
asyncSubscribe(WB_RES::LOCAL::MEAS_GYRO_SAMPLERATE(),
AsyncRequestOptions::Empty,
sampleRate);
}
void ExampleClient::onSubscribeResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::MEAS_GYRO_SAMPLERATE::LID:
{
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
void ExampleClient::onNotify(whiteboard::ResourceId resourceId,
const whiteboard::Value& value,
const whiteboard::ParameterList& parameters)
{
switch (resourceId.getConstId())
{
case WB_RES::LOCAL::MEAS_GYRO_SAMPLERATE::ID:
{
auto gyroData = value.convertTo<const WB_RES::GyroData&>();
// handle received data
DebugLogger::info("Gyro data, timestamp: %d", gyroData.timestamp);
}
break;
}
}
Sample application
Accelerometer application
Replacing everything related to Acc with Gyro should get you started.
/Meas/Magn
Link to up-to-date YAML specification
/Meas/Magn/Info : GET
/Meas/Magn/Config : GET, PUT
/Meas/Magn/{SampleRate} : SUBSCRIBE
Meas Magn API gives possibility to collect data from Magnetometer sensor.
Sensor capabilities
Magnetometer sensor is capable to deliver a lot of useful data for measuring the direction, strength, or relative change of a magnetic field at a particular location. It should be configured for each use case separately. It handles different working frequencies. For efficient work with magnetometer power consumption, magnetometer frequency should be taken into consideration.
To get info about available frequencies, use Meas/Magn/Info path:
WBCMD
wbcmd --port COM13 --path Meas/Magn/Info --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Meas/Magn/Info",
"content": {
"SampleRates": [
13,
26,
52,
104,
208,
416,
833,
1666
],
"Scale": [
5000
]
},
"querytimems": 40,
"querytimens": 40911264
}
Getting configuration
There is a possibility to check actual sensitivity configuration of the magnetometer. Please refer to example below:
WBCMD
wbcmd --port COM13 --path Meas/Magn/Config --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Meas/Magn/Config",
"content": {
"Scale": 5000
},
"querytimems": 31,
"querytimens": 31733842
}
The sensitivity scale of magnetometer cannot be adjusted with Movesense.
Changing configuration
Next example will show how to change the sensitivity range. It is possiblie to change it during an active subscription on the gyroscope data.
WBCMD
wbcmd --port COM13 --path Meas/Gyro/Config --op PUT --opdatatype GyroConfig --opdata '{"DPSRange":1000}'
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/174730000410/Meas/Gyro/Config",
"querytimems": 47,
"querytimens": 47791840
}
Internal access
#include <meas_gyro/resources.h>
...
void ExampleClient::setGyroscopeConfig()
{
WB_RES::GyroConfig gyroConfig;
gyroConfig.DPSRange = 1000;
asyncPut(WB_RES::LOCAL::MEAS_GYRO_CONFIG(),
AsyncRequestOptions::Empty,
gyroConfig);
}
void ExampleClient::onPutResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::MEAS_GYRO_CONFIG::LID:
{
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
Capturing gyroscope data
There are different ways of capturing data from gyroscope. Please note that using highest sample rates may be limited by the BLE bandwidth. Good practise is to collect raw data internally, process it and send only calculated result to the remote client.
WBCMD
wbcmd --port COM13 --path Meas/Gyro/52 --op SUBSCRIBE
Example notification:
@955 { {
"Timestamp": 108412,
"ArrayGyro": [
{
"x": 0.56000000238418579,
"y": -2.5199999809265137,
"z": -0.84000003337860107
}
]
} }
Internal access
#include <meas_gyro/resources.h>
...
void ExampleClient::subscribeGyro()
{
int32_t sampleRate = 52;
asyncSubscribe(WB_RES::LOCAL::MEAS_GYRO_SAMPLERATE(),
AsyncRequestOptions::Empty,
sampleRate);
}
void ExampleClient::onSubscribeResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::MEAS_GYRO_SAMPLERATE::LID:
{
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
void ExampleClient::onNotify(whiteboard::ResourceId resourceId,
const whiteboard::Value& value,
const whiteboard::ParameterList& parameters)
{
switch (resourceId.getConstId())
{
case WB_RES::LOCAL::MEAS_GYRO_SAMPLERATE::ID:
{
auto gyroData = value.convertTo<const WB_RES::GyroData&>();
// handle received data
DebugLogger::info("Gyro data, timestamp: %d", gyroData.timestamp);
}
break;
}
}
Sample application
Accelerometer application
Replacing everything related to Acc with Magn should get you started.
/Meas/IMU
Link to up-to-date YAML specification
/Meas/IMU/Info : GET
/Meas/IMU/Config : GET, PUT
/Meas/IMU6/{SampleRate} : SUBSCRIBE
/Meas/IMU6m/{SampleRate} : SUBSCRIBE
/Meas/IMU9/{SampleRate} : SUBSCRIBE
The Meas/IMU -API provides a synchronized access to accelerometer, gyroscope and magnetometer datastreams for easier processing e.g. for AHRS algorithms. It is more efficient to subscribe to the IMU resource than to subscribe the individual resources separately.
The available datastreams are:
- /Meas/IMU6: Combined Acc & Gyro
- /Meas/IMU6m: Combined Acc & Magn
- /Meas/IMU9: Combined Acc, Gyro & Magn
Sensor capabilities
To get info about available frequencies and sensitivities, use Meas/IMU/Info path:
WBCMD
wbcmd --port COM13 --path Meas/IMU/Info --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Meas/IMU/Info",
"content": {
"SampleRates": [
13,
26,
52,
104,
208,
416,
833,
1666
],
"AccRanges": [
2,
4,
8,
16
],
"GyroRanges": [
245,
500,
1000,
2000
],
"MagnRanges": [
5000
]
},
"querytimems": 40,
"querytimens": 40911264
}
Getting configuration
There is a possibility to get combined configuration using the Meas/IMU/Config -path. To set the configuration, use the Config -resource of individual sub sensors (/Meas/Acc/Confic & /Meas/Gyro/Config).
WBCMD
wbcmd --port COM13 --path Meas/IMU/Config --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Meas/IMU/Config",
"content": {
"AccRange": 8,
"GyroRange": 2000,
"MagnRange": 400
},
"querytimems": 31,
"querytimens": 31733842
}
Capturing IMU data
WBCMD
wbcmd --port COM13 --path Meas/IMU9/26 --op SUBSCRIBE
Example notification:
@955 { {
"Timestamp": 72519762,
"ArrayAcc": [{
"x": -0.021536173298954964,
"y": -0.17707519233226776,
"z": 9.9473190307617188
}, {
"x": -0.033500712364912033,
"y": -0.16750356554985046,
"z": 10.007142066955566
}
],
"ArrayGyro": [{
"x": -1.1200000047683716,
"y": -0.21000000834465027,
"z": 0.34999999403953552
}, {
"x": -1.3999999761581421,
"y": -0.14000000059604645,
"z": 0.34999999403953552
}
],
"ArrayMagn": [{
"x": -91.350006103515625,
"y": -256.5,
"z": -231.75001525878906
}, {
"x": -89.700004577636719,
"y": -254.40000915527344,
"z": -232.65000915527344
}
]
} }
Internal access
The internal access works identical to Acc, Gyro & Magn. The header file is included as:
#include <meas_imu/resources.h>
/Meas/ECG
Link to up-to-date YAML specification
/Meas/ECG/Info : GET
/Meas/ECG/Config : GET, PUT
/Meas/ECG/{RequiredSampleRate} : SUBSCRIBE
Movesense sensor is equipped with analog front-end capable of capturing ECG signals. ECG API allows to access these features, set the configuration and receive the results.
Sensor capabilities
To get information about the ECG sensor, do a GET to path Meas/ECG/Info. Returned structure presents currently active sample rate, list of sample rates supported by the sensor and number of samples contained in a single notification and available filter settings (new in v. 2.1):
WBCMD
wbcmd --port COM13 --path Meas/ECG/Info --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/meas/ecg/info",
"content": {
"CurrentSampleRate": 500,
"AvailableSampleRates": [
125,
128,
200,
250,
256,
500,
512
],
"ArraySize": 16,
"LowPass": [
40,
100,
150
],
"HighPass": [
0.5
]
},
"querytimems": 346,
"querytimens": 346459255
}
Getting configuration
[new in v.2.1]
Current ECG filter configuration can be received from the path Meas/ECG/Config.
Returned structure presents currently active low-pass and high-pass filter setting. If the filter is null, it has been set to bypass (i.e. no filter).
WBCMD
wbcmd --port COM13 --path Meas/ECG/Config --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/meas/ecg/config",
"content": {
"LowPass": 40,
"HighPass": null
},
"querytimems": 157,
"querytimens": 157680743
}
Changing configuration
[new in v.2.1]
Next example will show how to change the filter configuration. It is possible to change it during an active subscription on the ECG data.
NOTE: MAX30003 Analog frontend chip defaults to 100 Hz low-pass filter if the sampling rate is less than 500Hz and 40 Hz if the sampling rate is less than 250 Hz or if the sensor is not measuring.
WBCMD
wbcmd --port COM13 --path Meas/ECG/Config --op PUT --opdatatype ECGConfig --opdata '{"low-pass":150}'
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/174730000410/Meas/ECG/Config",
"content": "",
"querytimems": 47,
"querytimens": 47791840
}
Capturing ECG data
ECG subscriptions with high sample frequency (>256 Hz) over BLE should be avoided. The analog front end circuitry have relatively small FIFO (32 samples) and when ECG samples are transferred with high sample rate over BLE it is possible that FIFO is overflown. It is recommeded to perform the data processing on Movesense sensor if higher sample frequencies are needed.
Simultaneous subscriptions on different frequencies are forbidden; however, it is possible to subscribe single frequency multiple times.
WBCMD
wbcmd --port COM13 --path Meas/ECG/125 --op SUBSCRIBE
Example notification:
@534 { {
"Samples": [
890,
874,
1104,
1244,
1211,
1480,
1394,
1563,
1655,
1587,
1827,
1703,
1835,
1898,
1803,
2020
],
"Timestamp": 2894171
} }
Internal access
#include <meas_ecg/resources.h>
...
void ExampleClient::subscribeEcg()
{
int32_t sampleRate = 125;
asyncSubscribe(WB_RES::LOCAL::MEAS_ECG_REQUIREDSAMPLERATE(),
AsyncRequestOptions::Empty,
sampleRate);
}
void ExampleClient::onNotify(whiteboard::ResourceId resourceId,
const whiteboard::Value& value,
const whiteboard::ParameterList& parameters)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::MEAS_ECG_REQUIREDSAMPLERATE::LID:
{
auto ecgData = value.convertTo<const WB_RES::ECGData&>();
// handle received data
DebugLogger::info("ECG, timestamp: %d", ecgData.timestamp);
}
break;
}
}
/Meas/HR
Link to up-to-date YAML specification
/Meas/HR/Info : GET
/Meas/HR : SUBSCRIBE
Movesense sensor is equipped with analog front-end capable of capturing ECG signals and calculating user's heart rate. HR API allows to access to the heart rate measurement configuration and receive the results.
Configuration
Configuration of this module is read-only; i.e. only GET
requests are allowed on path /Meas/HR/Info
.
WBCMD
wbcmd --port COM13 --path Meas/HR/Info --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Meas/HR/Info",
"content": {
"Min": 200,
"Max": 2000,
"Accuracy": 5
},
"querytimems": 31,
"querytimens": 31809005
}
Measuring heart rate
WBCMD
wbcmd --port COM13 --path Meas/HR --op SUBSCRIBE
Example notification:
@2620 { {
"average": 50.890586853027344,
"rrData": [
1179
]
} }
Internal access
#include <meas_hr/resources.h>
#include <movesense_types/resources.h>
...
void ExampleClient::subscribeHr()
{
whiteboard::Result result = asyncSubscribe(WB_RES::LOCAL::MEAS_HR());
if (result != whiteboard::HTTP_CODE_OK)
{
// handle error
}
}
void ExampleClient::onNotify(whiteboard::ResourceId resourceId,
const whiteboard::Value& value,
const whiteboard::ParameterList& parameters)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::MEAS_HR::LID:
{
auto hrData = value.convertTo<const WB_RES::HRData&>();
// handle received data, note: received average value type is float
DebugLogger::info("HR average: %d", static_cast<uint32_t>(hrData.average));
}
break;
}
}
Sample application
Sample application using Meas/Hr API can be found in Movesense device repository.
HR standard service sample application
/Meas/Temperature
Link to up-to-date YAML specification
/Meas/Temp/Info : GET
/Meas/Temp : GET, SUBSCRIBE
Movesense sensor is equipped with temperature sensor, which can be used to measure device's internal temperature. Returned values are in units of Kelvins [K].
Range and accuracy
API provides information about accuracy and range of the temperature sensor used in the device.
WBCMD
$ wbcmd --port COM13 --path Meas/Temp/Info --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Meas/Temp/Info",
"content": {
"Min": 233,
"Max": 398,
"Accuracy": 1
},
"querytimems": 31,
"querytimens": 31975028
}
Single measurement
WBCMD
$ wbcmd --port COM13 --path Meas/Temp --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Meas/Temp",
"content": {
"Timestamp": 4424140,
"Measurement": 296.64999389648438
},
"querytimems": 79,
"querytimens": 79661821
}
Internal access
#include <meas_temp/resources.h>
...
void ExampleClient::getTemperature()
{
asyncGet(WB_RES::LOCAL::MEAS_TEMP());
}
void ExampleClient::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::MEAS_TEMP::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
auto temperature = resultData.convertTo<const WB_RES::TemperatureValue&>();
// handle measurement value
DebugLogger::info("Temperature: %d K", static_cast<int32_t>(temperature.measurement));
}
else
{
// handle error
}
break;
}
}
}
Continuous subscription to temperature
It is possible to subscribe to the sensor and receive notifications when temperature changes.
WBCMD
$ wbcmd --port COM13 --path Meas/Temp --op SUBSCRIBE
Example notification:
@1070 { {
"Timestamp": 12125,
"Measurement": 297.33749389648438
} }
Internal access
#include <meas_temp/resources.h>
...
void ExampleClient::subscribeTemperature()
{
asyncSubscribe(WB_RES::LOCAL::MEAS_TEMP());
}
void ExampleClient::onSubscribeResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::MEAS_TEMP::LID:
{
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
void ExampleClient::onNotify(whiteboard::ResourceId resourceId,
const whiteboard::Value& value,
const whiteboard::ParameterList& parameters)
{
switch (resourceId.getConstId())
{
case WB_RES::LOCAL::MEAS_TEMP::ID:
{
auto temperature = value.convertTo<const WB_RES::TemperatureValue&>();
// handle measurement value
DebugLogger::info("Temperature: %d K", static_cast<int32_t>(temperature.measurement));
}
break;
}
}
Sample application
Custom GATT service application is using Meas/Temp API and it can be found here:
Custom GATT service
/Misc/Manufacturing
Link to up-to-date YAML specification.
This is an internal API used when manufacturing the sensor.
/Component/Eeprom
Link to up-to-date YAML specification
/Component/Eeprom/{EepromIndex}/Info : GET
/Component/Eeprom/{EepromIndex} : GET, PUT
Eeprom API provides a direct access to the eeprom memory installed on Movesense sensor. There are two eeprom chips on Movesense sensor and API paths need EepromIndex parameter to point the proper chip.
Getting information about EEPROM chips
WBCMD
wbcmd --port COM10 --path Component/EEPROM/0/Info --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Component/EEPROM/0/Info",
"content": {
"Model": "AT24CM0x",
"Size": 131072
},
"querytimems": 31,
"querytimens": 31648617
}
Internal access
#include <component_eeprom/resources.h>
...
void ExampleClient::readEepromChipInfo()
{
uint8_t eepromIndex = 0;
asyncGet(WB_RES::LOCAL::COMPONENT_EEPROM_EEPROMINDEX_INFO(),
AsyncRequestOptions::Empty,
eepromIndex);
}
void ExampleClient::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::COMPONENT_EEPROM_EEPROMINDEX_INFO::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
auto info = resultData.convertTo<const WB_RES::EepromInfo&>();
// handle result
DebugLogger::info("EEPROM info, model: %s, size: %d",
static_cast<const char*>(info.model),
info.size);
}
else
{
// handle error
}
break;
}
}
}
Reading data from EEPROM
WBCMD
The eeprom chip is selected by the last part of the resource path. The first parameter of the get operation defines the memory address, the second one defines the number of bytes to be read.
wbcmd --port COM10 --path Component/EEPROM/0 --op GET --opdatatype uint32 --opdata 1024 --opdatatype uint8 --opdata 10
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/Component/EEPROM/0",
"content": {
"bytes": [
10,
20,
30,
40,
50,
60,
70,
80,
90,
100
]
},
"querytimems": 31,
"querytimens": 31558059
}
Internal access
#include <component_eeprom/resources.h>
...
void ExampleClient::readEepromData()
{
uint8_t eepromIndex = 0;
uint32_t address = 1024;
uint8_t length = 10;
asyncGet(WB_RES::LOCAL::COMPONENT_EEPROM_EEPROMINDEX(),
AsyncRequestOptions::Empty,
eepromIndex,
address,
length);
}
void ExampleClient::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::COMPONENT_EEPROM_EEPROMINDEX::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
auto data = resultData.convertTo<const WB_RES::EepromData&>();
// handle result
DebugLogger::info("EEPROM data:");
for (const uint8_t& byte : data.bytes)
{
DebugLogger::info(" %d", byte);
}
}
else
{
// handle error
}
break;
}
}
}
Writing data to EEPROM
Writing to the eeprom is not protected and can be performend on any part of the eeprom. It is good to use datalogger for writing data from sensors. Note: Direct eeprom API doesn't take care about switching chip indexes. It is impossible to write block of which starts at the end of the first eeprom chip and ends on the second eeprom chip. The written block has to be splited and written separately for each eeprom index
WBCMD
wbcmd --port COM10 --path Component/EEPROM/0 --op PUT --opdatatype uint32 --opdata 1024 --opdatatype EepromData --opdata '{"bytes":[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]}'
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/174730000410/Component/EEPROM/0",
"content": "",
"querytimems": 31,
"querytimens": 31589150
}
Internal access
#include <component_eeprom/resources.h>
...
void ExampleClient::writeEepromData()
{
uint8_t eepromIndex = 0;
uint32_t address = 1024;
uint8_t dataArray[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
WB_RES::EepromData eepromData = {.bytes = wb::MakeArray(dataArray)};
asyncPut(WB_RES::LOCAL::COMPONENT_EEPROM_EEPROMINDEX(),
AsyncRequestOptions::Empty,
eepromIndex,
address,
eepromData);
}
void ExampleClient::onPutResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::COMPONENT_EEPROM_EEPROMINDEX::LID:
{
DebugLogger::info("EEPROM data PUT, result: %d", resultCode);
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
/Component/Led{s}
Link to up-to-date YAML specification
/Component/Led : PUT - DEPRECATED
/Component/Leds : GET
/Component/Leds/{LedIndex} : GET, PUT
Leds API is an interface for turn on and off the LED
Getting current state
GET operation can be used with Component/Leds and Component/Leds/{LedIndex}. First one will response with list of all available leds and their colors. Second provide information only about specific one. Since Movesense sensor has only one red LED LedIndex 0 is the only available.
WBCMD
Getting state of all available LEDs:
wbcmd --port COM10 --path component/leds
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/ECKI8F32D345/component/leds",
"content": {
"LedStates": [
{
"IsOn": false,
"LedColor": [
0,
"Red"
]
}
]
},
"querytimems": 38,
"querytimens": 38211895
}
Getting state of a single LED:
wbcmd --port COM10 --path component/leds/0
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/ECKI8F32D345/component/leds/0",
"content": {
"IsOn": false,
"LedColor": [
0,
"Red"
]
},
"querytimems": 25,
"querytimens": 25553917
}
Internal access
Example 1 (all available LEDs):
#include <component_led/resources.h>
...
void ExampleClient::checkAvailableLeds()
{
asyncGet(WB_RES::LOCAL::COMPONENT_LEDS());
}
void ExampleClient::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::COMPONENT_LEDS::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
auto params = resultData.convertTo<const WB_RES::Leds&>();
// handle state
DebugLogger::info("Leds GET result, Led 0 state: %d", params.ledStates[0].isOn);
}
else
{
// handle error
}
break;
}
}
}
Example 2 (specific LED):
#include <component_led/resources.h>
...
void ExampleClient::checkLed0()
{
asyncGet(WB_RES::LOCAL::COMPONENT_LEDS_LEDINDEX(),
AsyncRequestOptions::Empty,
(int32)0); //0 is an index of Movesense LED
}
void ExampleClient::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::COMPONENT_LEDS_LEDINDEX::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
auto state = resultData.convertTo<const WB_RES::LedState&>();
// handle state
DebugLogger::info("Leds index 0 result, Led state: %d", state.isOn);
}
else
{
// handle error
}
break;
}
}
}
Setting state
PUT is available only for path with LedIndex. For current Movesense sensor the LedIndex is always 0. There is a need to provide state (IsOn) and optionally color (LedColor). For current Movesense sensor there is only one color available - red (0)
WBCMD
wbcmd --port COM10 --path component/leds/0 --op put --opdatatype LedState --opdata '{"IsOn":true,"LedColor":0}'
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/ECKI8F32D345/component/leds/0",
"content": "",
"querytimems": 25,
"querytimens": 25729157
}
Internal access
In case of passing multiple parameters within a single request, path parameters come first (in this case, ledIndex
), and then body parameters.
#include <component_led/resources.h>
...
void ExampleClient::setLed()
{
int32_t ledIndex = 0;
WB_RES::LedState config;
config.isOn = true;
asyncPut(WB_RES::LOCAL::COMPONENT_LEDS_LEDINDEX(),
AsyncRequestOptions::Empty,
ledIndex,
config);
}
void ExampleClient::onPutResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::COMPONENT_LEDS_LEDINDEX::LID:
{
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
Sample applications
Sample applications using Component/Led API can be found here:
HR standard service sample application
HR wakeup app
Secure BLE app
/Ui/Ind/Visual
Link to up-to-date YAML specification
This provider allows to use the red LED available in the device in order to provide visual indications to the user. According to the API, it offers 3 working modes:
- 0: no visual indications,
- 1: continuous blinking indication,
- 2: short, one-time blink.
Getting current indication state
WBCMD
$ wbcmd --port COM13 --path /Ui/Ind/Visual --op GET
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/174730000410/ui/ind/visual",
"content": {
"state": 0
},
"querytimems": 31,
"querytimens": 31948776
}
INTERNAL ACCESS
#include <ui_ind/resources.h>
...
void ExampleClient::getIndicationState() {
asyncGet(WB_RES::LOCAL::UI_IND_VISUAL());
}
void ExampleClient::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::UI_IND_VISUAL::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
const WB_RES::VisualIndState& indicationState = resultData.convertTo<const WB_RES::VisualIndState&>();
//handleResult(indicationState.state);
}
break;
}
}
}
Setting new indication state
These examples assume that the continuous blinking mode is requested (value 1
). In order to set different mode, change the parameter's value.
WBCMD
$ wbcmd --port COM13 --path /Ui/Ind/Visual --op PUT --opdatatype uint16 --opdata 1
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/174730000410/ui/ind/visual",
"content": "",
"querytimems": 31,
"querytimens": 31778829
}
INTERNAL ACCESS
#include <ui_ind/resources.h>
...
uint16_t newState = 1;
asyncPut(WB_RES::LOCAL::UI_IND_VISUAL(),
AsyncRequestOptions::Empty,
newState);
/Component/MAX3000x
Link to up-to-date YAML specification
/Component/MAX3000x/WakeUp : GET, PUT
/Component/MAX3000x/Version : GET
/Component/MAX3000x/Register : GET, PUT
MAX3000x API is a low level interface for controlling the analog front-end (AFE). AFE is used for ECG and Heart Rate calculation in Movesense sensor. It is also used for lead connection detection for GearId tags and wakup trigger.
Please notice that this is low level component API. It is strongly recommended to use Meas API instead of Component API if possible.
Setup Wakeup from AFE
This API can be used to configure the device waking up from power off by touching AFE pins.
GET state:
WBCMD
wbcmd --port COM10 --path Component/MAX3000x/WakeUp
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/ECKI8F32D345/Component/MAX3000x/Wakeup",
"content": 0,
"querytimems": 27,
"querytimens": 27417004
}
Internal access
Example:
#include <component_max3000x/resources.h>
...
void ExampleClient::getWakeupConfig()
{
asyncGet(WB_RES::LOCAL::COMPONENT_MAX3000X_WAKEUP());
}
void ExampleClient::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::COMPONENT_MAX3000X_WAKEUP::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
auto wakeup = resultData.convertTo<const uint8_t&>();
// handle state
DebugLogger::info("Wakeup config: %d", wakeup);
}
else
{
// handle error
}
break;
}
}
}
SET new state:
WBCMD
wbcmd --port COM10 --path Component/MAX3000x/WakeUp --op put --opdatatype uint8 --opdata 1
Response:
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/ECKI8F32D345/Component/MAX3000x/WakeUp",
"content": "1",
"querytimems": 27,
"querytimens": 27943739
Internal access
Example:
#include <component_max3000x/resources.h>
...
void ExampleClient::setWakeupConfig()
{
uint8_t wakeup = 1;
asyncPut(WB_RES::LOCAL::COMPONENT_MAX3000X_WAKEUP(),
AsyncRequestOptions::Empty,
wakeup);
}
void ExampleClient::onPutResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::COMPONENT_MAX3000X_WAKEUP::LID:
{
DebugLogger::info("Wakeup PUT result: %d", resultCode);
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
GET chipset vesrion
You can read actual version of AFE chipset
WBCMD
wbcmd --port COM10 --path Component/MAX3000x/Version
Response:
{
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/ECKI8F32D345/Component/MAX3000x/Version",
"content": {
"versionInfo": [
{
"name": "Revision",
"version": "1"
},
{
"name": "Part",
"version": "3"
}
]
},
"querytimems": 40,
"querytimens": 40032649
}
Internal access
#include <component_max3000x/resources.h>
...
void ExampleClient::getMAX3000xVersion()
{
asyncGet(WB_RES::LOCAL::COMPONENT_MAX3000X_VERSION());
}
void ExampleClient::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::COMPONENT_MAX3000X_VERSION::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
auto versionInfoArray = resultData.convertTo<const WB_RES::VersionInfoArray&>();
// handle version
for (const WB_RES::VersionInfo& info : versionInfoArray.versionInfo)
{
DebugLogger::info("MAX3000x %s: %s",
static_cast<const char*>(info.name),
static_cast<const char*>(info.version));
}
}
else
{
// handle error
}
break;
}
}
}
Write or read to the chipset registers directly
It is possible to customize and prepare own register setup. The path /Component/MAX3000x/Register gives access to internal chipset registers.
WBCMD
For GET register status:
wbcmd --port COM10 --path Component/MAX3000x/Register --opdatatype uint8 --opdata 10
Response:
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "get",
"uri": "/net/ECKI8F32D345/Component/MAX3000x/Register",
"content": 0,
"querytimems": 27,
"querytimens": 27946153
To PUT new value into register:
wbcmd --port COM10 --path Component/MAX3000x/Register --op put --opdatatype uint8 --opdata 10 --opdatatype uint32 --opdata 0
Response:
"response": 200,
"responsestring": "HTTP_CODE_OK",
"operation": "put",
"uri": "/net/ECKI8F32D345/Component/MAX3000x/Register",
"content": "",
"querytimems": 27,
"querytimens": 27774097
Internal access
#include <component_max3000x/resources.h>
...
void ExampleClient::readMAX3000xRegister()
{
uint8_t registerAddress = 10;
asyncGet(WB_RES::LOCAL::COMPONENT_MAX3000X_REGISTER(),
AsyncRequestOptions::Empty,
registerAddress);
}
void ExampleClient::writeMAX3000xRegister()
{
uint8_t registerAddress = 10;
uint32_t value = 0;
asyncPut(WB_RES::LOCAL::COMPONENT_MAX3000X_REGISTER(),
AsyncRequestOptions::Empty,
registerAddress,
value);
}
void ExampleClient::onGetResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::COMPONENT_MAX3000X_REGISTER::LID:
{
if (resultCode == whiteboard::HTTP_CODE_OK)
{
auto value = resultData.convertTo<const uint32_t&>();
// handle value
DebugLogger::info("MAX3000x register GET, value: %d", value);
}
else
{
// handle error
}
break;
}
}
}
void ExampleClient::onPutResult(whiteboard::RequestId requestId,
whiteboard::ResourceId resourceId,
whiteboard::Result resultCode,
const whiteboard::Value& resultData)
{
switch (resourceId.localResourceId)
{
case WB_RES::LOCAL::COMPONENT_MAX3000X_REGISTER::LID:
{
DebugLogger::info("MAX3000x register PUT, result: %d", resultCode);
if (resultCode != whiteboard::HTTP_CODE_OK)
{
// handle error
}
break;
}
}
}
Sample applications
Sample applications using Component/MAX3000x/WakeUp API can be found here: