Getting started with Sensor SW


These instructions help you to get started with Movesense sensor software development. This section is split into three parts.


Toolchain and usage

Software requirements

The following tools need to be installed and added to the system PATH in order to run the examples in this document.

IMPORTANT!! Builds on Mac OSX are broken on 1.9 due to a compiler bug! We're preparing a docker build environment to avoid these issues in the future. So these following instructions apply only to Windows environment.

Mandatory

Optional

Movesense device library is available here.

Build commands (Real HW)

To successfully build the Movesense software it's required to clone the Movesense-device-lib repository:

git clone git@bitbucket.org:suunto/movesense-device-lib.git

After that go to the cloned repository and create there build directory:

cd movesense-device-lib
mkdir myBuild
cd myBuild

Now, using the CMake, it's possible to build both the debug and release version. In both cases the command will contain the following:

cmake -G Ninja -DMOVESENSE_CORE_LIBRARY=../MovesenseCoreLib/ -DCMAKE_TOOLCHAIN_FILE=../MovesenseCoreLib/toolchain/gcc-nrf52.cmake <sample_directory>

But to build a release version it's neccessary to specify the CMAKE_BUILD_TYPE option with the Release value:

cmake -G Ninja -DMOVESENSE_CORE_LIBRARY=../MovesenseCoreLib/ -DCMAKE_TOOLCHAIN_FILE=../MovesenseCoreLib/toolchain/gcc-nrf52.cmake -DCMAKE_BUILD_TYPE=Release <sample_directory>

Note that the means the relative path to the CMakeLists.txt file of the application desired to be built. For example: ../samples/hello_world_app

After that only the last step remains:

ninja

The created Movesense.hex file can be found in the same directory.

Build commands (Sensor Simulator)

Starting on Movesense device library v1.7.0 it is possible to compile and run your sensor software in the Sensor Simulator. For more info, see the Sensor Simulator -document. To build the Movesense software for Sensor Simulator, clone the repository like above and create a build directory:

cd movesense-device-lib
mkdir simuBuild
cd simuBuild

Now create the Visual Studio solution using the CMake. The generator you should use depends on which version of Visual Studio you have installed (2015 or 2017):

cmake -G "Visual Studio 14 2015" -DMOVESENSE_CORE_LIBRARY=../MovesenseCoreLib/ <sample_directory>

or for Visual Studio 2017:

cmake -G "Visual Studio 15 2017" -DMOVESENSE_CORE_LIBRARY=../MovesenseCoreLib/ <sample_directory>

To build a release version, specify the CMAKE_BUILD_TYPE option with the Release value:

cmake -G "Visual Studio 14 2015" -DMOVESENSE_CORE_LIBRARY=../MovesenseCoreLib/ -DCMAKE_BUILD_TYPE=Release <sample_directory>

Note that the means the relative path to the CMakeLists.txt file of the application desired to be built. For example: ../samples/blinky_app

The cmake will generate the Visual Studio solution-file (Project.sln) in the build directory. Just open it with the Visual Studio IDE, select "Movesense" project and "Debug" it.


Description of main components

App.cpp

#include "movesense.h"

Movesense.h contains necessary macros and definitions.


MOVESENSE_APPLICATION_STACKSIZE(1024)

You can define the application stack size. If you do not know how it works, do not change it.


MOVESENSE_PROVIDERS_BEGIN(0)
MOVESENSE_PROVIDERS_END(0)

MOVESENSE_PROVIDERS_BEGIN and MOVESENSE_PROVIDERS_END contains the definitions of the new providers. See "Creating own data provider" for more informations.


MOVESENSE_FEATURES_BEGIN()

The Movesense library has possibility to change the state of some providers and configure them. It can help you save some extra memory for your application.


// Explicitly enable or disable Movesense framework core modules.
// List of modules and their default state is found in documentation
OPTIONAL_CORE_MODULE(DataLogger, true)
OPTIONAL_CORE_MODULE(Logbook, true)
OPTIONAL_CORE_MODULE(LedService, true)
OPTIONAL_CORE_MODULE(IndicationService, true)
OPTIONAL_CORE_MODULE(BleService, true)
OPTIONAL_CORE_MODULE(EepromService, true)
OPTIONAL_CORE_MODULE(BypassService, false)

// NOTE: It is inadvisable to enable both Logbook/DataLogger and EepromService without 
// explicit definition of Logbook memory are (see LOGBOOK_MEMORY_AREA macro in movesense.h and eeprom_logbook_app).
// Default setting is for Logbook to use the whole EEPROM memory area.
// LOGBOOK_MEMORY_AREA(offset, size);

This part is connected to optional modules. You can disable or enable them. To see all Optional modules and default values for them you can see this document.


APPINFO_NAME("Sample Plain");
APPINFO_VERSION("1.0.0");
APPINFO_COMPANY("Movesense");

It is dedicated part for your application. You can easily check what is installed on the Movesense and version of your application.

Check /Info API.


SERIAL_COMMUNICATION(false) // Warning: enabling this feature will increase power consumption
BLE_COMMUNICATION(true)

You can use SERIAL_COMMUNICATION if you have JIG and debugger but it drains the battery. BLE_COMMUNICATION should be always true.


MOVESENSE_FEATURES_END()

app_root.yaml

This yaml file contains configuration for the Whiteboard which is a part of the Movesense. You should not change the values if you do not understand them - it can change the stability of the platform.

# Type of the document
wbres:
  version: "2.0"
  type: "root"

# Execution context definitions
executionContexts:
  application:
    numberOfDpcs: 8
    numberOfRequests: 20
    numberOfResponses: 25
    externalThread: true # we run this execution context in main thread
    stackSize: 768
    priority: normal
  meas:
    numberOfDpcs: 9
    numberOfRequests: 25
    numberOfResponses: 10
    stackSize: 768
    priority: normal

Example Project

Suggested way of working with our library is not mandatory, but it helps to setup environment and to create first project.

Setting up the project

  1. Create git repository:
  $ mkdir myproject  
  $ cd myproject  
  $ git init  
  1. Add submodule:
  $ git submodule add git@bitbucket.org:suunto/movesense-device-lib.git movesense-device-lib --depth 1 --force
  1. Copy plain application sample to main directory:
  $ cp -R movesense-device-lib/samples/plain_app/* ./

Or optionally on mingw:

  $ cp movesense-device-lib/samples/plain_app/* ./ -R
  1. Modify CMakeLists.txt to use correct build path (line 8):
   set(BUILD_CONFIG_PATH ${CMAKE_CURRENT_SOURCE_DIR}/movesense-device-lib/samples/_build)
  1. Compile
  2. create and enter build dir:
    $ mkdir builddir
    $ cd builddir
  3. generate ninja files:
    $ cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=../movesense-device-lib/MovesenseCoreLib/toolchain/gcc-nrf52.cmake -DMOVESENSE_CORE_LIBRARY=../movesense-device-lib/MovesenseCoreLib ../ -DCMAKE_BUILD_TYPE=Debug ../
  4. run ninja:
    $ ninja

  5. Test
    Run ninja dfupkg and use the resulting zip file to update the firmware on movesense sensor with your phone (DFU).
    Alternatively, run ninja flash to flash using programming jig.

  6. Great job - your project is now ready for development!

Creating own data provider

It is good practise to calculate on the Movesense to avoid additional BLE communication. So if you want to do some application which ex. is counting steps you should put the algorithm on the Movesense and send only result of the calculation to the mobile phone.

Creating API yaml file

The first step is to create the API yaml file. You can see the sample apps and the Movesense API.

API below contains only one path and only GET function which will be returning always value.

Test.yaml

swagger: '2.0'

info:
  version: "0.0.0"
  title: Test API
  description: |
    Description
  termsOfService: http://xxxxx.com/
  contact:
    name: ABCD Company
    url: http://COMPANYABCD.com

# Paths
paths:
  /Sample/Test:
    get:
      description: |
        Test function returning always 1
      responses:
        200:
          schema:
            description: New value
            $ref: '#/definitions/Test'

definitions:
  Test:
    required:
      - Value
    properties:
      Value:
        description: Some description
        type: integer
        format: uint8

Changing app_root.yaml

apis:
  Test.*:
    apiId: 100
    defaultExecutionContext: meas

Simple TestService Implementation

TestService.hpp

#pragma once

#include "app-resources/resources.h"
#include <whiteboard/LaunchableModule.h>
#include <whiteboard/ResourceProvider.h>

class TestService FINAL : private whiteboard::ResourceProvider,
                          public whiteboard::LaunchableModule

Service class should extend whiteboard::ResourceProvider and whiteboard::LaunchableModule classes.


{
public:
    static const char* const LAUNCHABLE_NAME;
    TestService();
    ~TestService();

private:
    virtual bool initModule() OVERRIDE;
    virtual void deinitModule() OVERRIDE;
    virtual bool startModule() OVERRIDE { mModuleState = WB_RES::ModuleStateValues::STARTED; return true;}
    virtual void stopModule() OVERRIDE { mModuleState = WB_RES::ModuleStateValues::STOPPED; }

It is a good practise to OVERRIDE all state functions. You can check implementation of initModule and deinitModule in TestService.cpp file.


    virtual void onGetRequest(const whiteboard::Request& request,
                              const whiteboard::ParameterList& parameters) OVERRIDE;
};

onGetRequest is called when you will request data from the Movesense ( Sample/Test GET from yaml file). You can check other functions like onPutRequest, onSubscribe, ... in the Whiteboard documentation.

TestService.cpp

#include "movesense.h"
#include "TestService.hpp"
#include "app-resources/resources.h"

const char* const TestService::LAUNCHABLE_NAME = "TestSvc";

static const whiteboard::LocalResourceId sProviderResources[] = {
    WB_RES::LOCAL::SAMPLE_TEST::LID,
};

TestService::TestService()
    : ResourceProvider(WBDEBUG_NAME(__FUNCTION__), WB_RES::LOCAL::SAMPLE_TEST::EXECUTION_CONTEXT),
      LaunchableModule(LAUNCHABLE_NAME, WB_RES::LOCAL::SAMPLE_TEST::EXECUTION_CONTEXT)
{}

TestService::~TestService()
{}

bool TestService::initModule()
{
    if (registerProviderResources(sProviderResources) != whiteboard::HTTP_CODE_OK)
    {
        return false;
    }

    mModuleState = WB_RES::ModuleStateValues::INITIALIZED;
    return true;
}

void TestService::deinitModule()
{
    unregisterProviderResources(sProviderResources);
    mModuleState = WB_RES::ModuleStateValues::UNINITIALIZED;
}


void TestService::onGetRequest(const whiteboard::Request& request,
                                     const whiteboard::ParameterList& parameters)
{

    if (mModuleState != WB_RES::ModuleStateValues::STARTED)
    {
        return returnResult(request, wb::HTTP_CODE_SERVICE_UNAVAILABLE);
    }

    switch (request.getResourceConstId())
    {
    case WB_RES::LOCAL::SAMPLE_TEST::ID:
    {
        WB_RES::Test test;
        test.value = 1;
        return returnResult(request, whiteboard::HTTP_CODE_OK, ResponseOptions::Empty, test);
    }

    break;

    default:
        return returnResult(request, whiteboard::HTTP_CODE_NOT_FOUND);
    }
}

Update App.cpp

The last change necessary to complete our simple app.

#include "TestService.hpp"
MOVESENSE_PROVIDERS_BEGIN(1)
MOVESENSE_PROVIDER_DEF(TestService)
MOVESENSE_PROVIDERS_END(1)

MOVESENSE_PROVIDERS_BEGIN and MOVESENSE_PROVIDERS_END should be incremented and the new service added.