Skip to main content

Pinnacle™ 100 Modem Integration Guide

In this guide we will walk through the steps for integrating the Memfault Firmware SDK into any project using Laird's Pinnacle™ 100 Cellular Modem (i.e Sentrius™ MG100).

tip

The Pinnacle-100-Firmware has built-in support for the Memfault Firmware SDK. Laird Connectivity has released some excellent documentation on how to integrate Memfault and the AWS Out-of-Box demo includes a full Memfault integration that can be tried out. We recommend following both this documentation page as well as Laird's.

Upon completion of the integration, the following subcomponents will be added to your system!

Integration Steps#

This tutorial assumes you have a working environment for building the Pinnacle 100 Firmware (such as an an application based on the AWS Out-of-Box Demo).

Create a Memfault Account#

The integration requires a Memfault cloud account to process data collected by the Memfault Firmware SDK. You can create one for free from here!

Update Kconfig selections in prj.con#

Add the following options to your system's prj.conf:

# Project-specific configuration settings
CONFIG_MEMFAULT_NCS_PROJECT_KEY="YOUR_PROJECT_KEY"
CONFIG_MEMFAULT_NCS_FW_VERSION_PREFIX="1.0.0+"
CONFIG_MEMFAULT_NCS_FW_TYPE="pinnacle-fw"

Publish data to the Memfault cloud#

HTTPS#

The integration will automatically push data to Memfault over HTTPS when an LTE connection becomes available prior to connecting to AWS IoT allowing one to immediately get information about unexpected resets as well as debug any connectivity issues with AWS IoT.

MQTT#

After a connection with AWS IoT is established, Memfault data is published to the following topic.

prod/<board>/<device_id>/memfault/<memfault_project_key>/chunk

Forwarding data to Memfault from AWS IoT is very easy! You just need to configure an AWS IoT Rule for the topic and connect it to an AWS Lambda function. This can either be done manually in the AWS console or programmatically using CloudFormation.

caution

These steps assume you have installed the AWS CLI and have set up credentials to access your AWS instance.

  1. Copy and paste the contents below into cfnTemplate_memfault.yml
  2. Deploy the configuration
$ aws cloudformation deploy --template cfnTemplate_memfault.yml --stack-name memfault-pinnacle-100-chunk-proxy --capabilities CAPABILITY_NAMED_IAM
Waiting for changeset to be created..
Waiting for stack create/update to complete

cfnTemplate_memfault.yml file contents#

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Memfault Pinnacle 100 Chunk Proxy
Parameters:
ApplicationName:
Type: String
Default: memfault-chunk-proxy
Resources:
LambdaIotRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${ApplicationName}-lambda-iot-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: !Sub /${ApplicationName}/
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSIoTFullAccess
Policies:
- PolicyName: LambdaIotPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- arn:aws:logs:*:*:log-group:/aws/lambda/*
#
# Proxy Memfault "chunks" to Memfault cloud for processing
# For more details about data transport see https://mflt.io/data-to-cloud
#
MemfaultProxyFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: "MemfaultPinnacle100ProxyFunction"
Code:
ZipFile: |
const https = require('https')
exports.https = https
exports.handler = (event, context, callback) => {
const data = Buffer.from(event.data, 'base64');
// Topic Format: prod/<board>/<device_id>/memfault/<memfault_project_key>/chunk
const topicParams = event.topic.split('/')
const deviceSerial = topicParams[2]
const options = {
hostname: 'chunks.memfault.com',
port: 443,
path: `/api/v0/chunks/${deviceSerial}`,
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream',
'Content-Length': data.length,
'Memfault-Project-Key': topicParams[4]
}
}
const req = https.request(options, res => {
const response = {
statusCode: res.statusCode
};
callback(null, response);
})
req.on('error', error => {
console.error(error)
})
req.write(data)
req.end()
}
Handler: "index.handler"
Role: !GetAtt LambdaIotRole.Arn
Runtime: nodejs12.x
Timeout: 15
Environment:
Variables:
ApplicationName: !Ref ApplicationName
MemfaultProxyFunctionLogGroup:
Type: AWS::Logs::LogGroup
DependsOn: MemfaultProxyFunction
Properties:
RetentionInDays: 14
LogGroupName: !Join ["", ["/aws/lambda/", !Ref MemfaultProxyFunction]]
MemfaultProxyFunctionPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt
- MemfaultProxyFunction
- Arn
Principal: iot.amazonaws.com
SourceArn: !Sub arn:aws:iot:${AWS::Region}:${AWS::AccountId}:rule/${MemfaultProxyRule}
MemfaultProxyRule:
Type: AWS::IoT::TopicRule
Properties:
RuleName: "MemfaultPinnacle100ProxyRule"
TopicRulePayload:
AwsIotSqlVersion: "2016-03-23"
RuleDisabled: false
Sql:
!Sub SELECT encode(*, 'base64') AS data, topic() AS topic FROM
'prod/+/+/memfault/#'
Actions:
- Lambda:
FunctionArn: !GetAtt MemfaultProxyFunction.Arn