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.

  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
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
  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 operation requires additional parameters when called using the wbcmd tool:

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:

  1. Install ADB and drivers for the Android phone
  2. Install sample application
    ex. $ adb install sampleapp-debug-1.8.0.apk
  3. Open application and connect to Movesense sensor
  4. 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
  5. 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.

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.

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.

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:

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:

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:

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:

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:

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:

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:

HR standard service sample application

HR wakeup app

Secure BLE app