MCUmgr Bluetooth protocol (SMP)
About
SMP Service
Simple Management Protocol
SMP Header
Payload
Commands
Device Firmware Upgrade
Modes
Test and Confirm
Test only
Confirm only
Algorithm
Mobile clients
About
Implementation of mcumgr may be found here: https://github.com/apache/mynewt-mcumgr
Supported are Mynewt and Zephyr.
SMP Service
SMP stands for Simple Management Protocol.
The GATT SMP service details:
Overview Characteristics
Name:
SMP Service Overview Properties Security Descriptors
UUID: Name: None
8D53DC1D-1DB7-4CD3-868B-8A527460AA84 SMP
Characteristic Prope Re Overview
rty qui
UUID: re Client
me Characteristic
Requirement: nt Configuration
DA2E7828-FBCE-
4E01-AE9E- Descriptor
Mandatory 261174997C48 Read Excl
uded UUID:
Requirement:
Write Excl 0x2902
Mandatory uded
Type:
WriteWi Man
thoutRe dato https://www.
sponse ry bluetooth.com
/specifications
Signed Excl /gatt/viewer?
Write uded attributeXmlFile
=org.bluetooth.
Notify Man descriptor.gatt.
dato client_characte
ry ristic_configura
tion.xml
Indicate Excl
uded Requirement:
Writable Excl Mandatory
Auxiliari uded
es
Broadca Excl
st uded
Extende
dProper
ties
Simple Management Protocol
Each SMP packet consists of 8-byte header and optional payload.
The server must respond to each SMP packet with a notification.
SMP Header
Bytes Comment Possible Values
0 Operation.
READ 0
An outgoing command will either be a
READ or a WRITE READ RSP 1
which will trigger a READ RSP or WRITE
RSP from the end device. WRITE 2
Big Endian WRITE RSP 3
Links:
https://github.com/apache/mynewt-mcumgr
/blob/master/mgmt/include/mgmt/mgmt.h
1 Optional flags fro this command. N/A
Not used. Shall be 0.
2-3 Payload length, in bytes.
UINT16, Big Endian.
4-5 Group ID.
The Command ID (see below) references
OS* 0
the subcommand of this group.
Group IDs up to unsigned integer 64 are IMAGE* 1
reserved for system level McuMgr
commands. Per-user commands can be STAT* 2
implemented for Group IDs above 64.
CONFIG 3
UINT16, Big Endian.
LOG 4
Links:
CRASH 5
https://github.com/apache/mynewt-mcumgr
/blob/master/mgmt/include/mgmt/mgmt.h - SPLIT 6
group IDs
RUN 7
https://github.com/apache/mynewt-mcumgr
/tree/master/cmd - groups implementation FS* 8
folder
PER USER 64
* Partially or fully implemented in Zephyr
6 Sequence Number. N/A
The sequence number in the request shall
match sequence number in the response.
This allows, in theory, to sent multiple
requests one after each other without
waiting for the response, and then receiving
responses with matching sequence
numbers. However, this does not guarantee
that the protocol sent using this mechanism
will support it, e.g. DFU in NCS 1.7 seems
to be sending wrong responses (incorrect
offset) even though the sequence numbers
are returned correctly, making the DFU
much slower due to unnecessary
repetitions. In Android library this may be
utilized using https://github.com
/NordicSemiconductor/Android-nRF-
Connect-Device-Manager/blob
/a61a0294fb4241ffcf4c91621c1a8ede3d4c4
643/mcumgr-core/src/main/java/io/runtime
/mcumgr/dfu/FirmwareUpgradeManager.
java#L308
7 Command ID. See below.
The Command ID identifies the
subcommand for the group set by Group ID
field.
Command IDs may be found in https://github
.com/apache/mynewt-mcumgr/tree/master
/cmd Group include [group]_mgmt.h
Payload
Payload shall be encoded using CBOR protocol. CBOR is a JSON but for embedded.
Link: http://cbor.io
CBOR 2 JSON converter: http://cbor.me
Example:
JSON:
{ "len": 11, "offset": 0, "data": "hello world" }
CBOR:
A3 # map(3)
63 # text(3)
6C656E # "len"
0B # unsigned(11)
66 # text(6)
6F6666736574 # "offset"
00 # unsigned(0)
64 # text(4)
64617461 # "data"
6B # text(11)
68656C6C6F20776F726C64 # "hello world"
Links:
http://cbor.me/?diag={%20%22len%22:%2011,%20%22offset%22:%200,%20%22data%22:%20%22hello%20world%22%20}
Commands
Each Command ID requires some fields in the packet and responds with some other. Here's the full list of all groups, commands and their
parameters that are supported by Zephyr or Mynewt.
Group ID Command ID Operation Required fields Fields in response
OS (0) ECHO (0) WRITE d - String r - String
Links: Links:
os_mgmt.h os_mgmt.c Example: Example:
{"d":"Hello!"} {"r":"Hello!"}
CONSOLE ECHO CTRL (1)
TASKSTAT (2) READ N/A tasks - map of:
Not implemented in Zephyr prio - uint
tid - uint
state - uint
stkuse - uint
stksiz - uint
cswcnt - uint
runtime - uint
last_checkin - uint
next_checkin - uint
MPSTAT (3)
DATETIME STR (4)
RESET (5) WRITE N/A N/A
IMAGE (1) STATE (0) READ N/A images - map of:
Links: Links: image - uint (image number
(core id), mainly for nRF53,
img_mgmt.h img_mgmt.c otherwise 0 or absent). Since
NCS 1.7.
slot - uint (0 (primary) or 1
(secondary))
version - String (any,
unknown length limit)
hash - byte array (SHA-256 of
the image)
bootable - boolean
pending - boolean (true if test
or confirm cmd has been sent
to that image)
confirmed - boolean (true if
the image has booted
(confirmed) successfully)
active - boolean (true if
currently active, running)
permanent - boolean (true if
confirm cmd has been sent to
that image)
splitStatus - uint (only used in
Mynewt)
WRITE hash - byte array (optional if Same as for READ
confirm = true to confirm the
currently running image)
confirm - boolean
UPLOAD (1) WRITE data - byte array rc - uint
len - uint (only when off = 0) off - uint
off - uint
image - image number (core id),
Since NCS 1.7.
sha - byte array (3 first bytes of
32 byte long SHA-256) (impl in
Java)
FILE (2)
CORELIST (3)
CORELOAD (4)
ERASE (5) WRITE image - image number (core id), rc - uint
Since NCS 1.7.
ERASE STATE (6)
Links:
Android library - I couldn't find it
anywhere else
iOS Library - same here
STAT (2) SHOW (0) READ name - String rc - uint
Links: Links: name - String
stat_mgmt.h stat_mgmt.c fields - map of:
[entry name] - uint
LIST (1) READ N/A stat_list - array or Strings
CONFIG (3)
LOG (4) SHOW (0) READ log_name - String next_index - uint
Links: Links: ts - timestamp (int) logs - array of:
log_mgmt.h log_mgmt.c index - uint name - String
type - uint
Not implemented in Zephyr entries - array of:
msg - String
ts - timestamp (int)
level - uint
index - uint
module - uint
rc - uint
CLEAR (1) WRITE log_name - String
rc - uint
APPEND (2)
MODULE LIST (3) READ N/A rc - uint
module_map - map of:
[module name] - uint
LEVEL LIST (4) READ N/A rc - uint
level_map - map of:
[level name] - uint (level)
LOGS LIST (5) READ N/A rc - uint
log_list - array of Strings (log
names)
CRASH (5)
SPLIT (6)
RUN (7)
FS (8) FILE (0) READ name - String data - byte array
Links: Links: off - uint len - uint (only when off = 0)
fs_mgmt.h fs_mgmt.c off - uint
rc - uint
WRITE name - String rc - uint
data - byte array off - uint
len - uint (only when off = 0)
off - uint
BASIC (63) - Added in NCS 1.7 ERASE_STORAGE (0) WRITE N/A rc - uint
to erase app settings
Links:
Links:
basic_mgmt.c
zephyr_groups.h
PERUSER (64) - reserved for
users
Errors
An invalid command or an error on the device will be reported with a notification with rc (uint) field in the payload.
List of possible return codes:
Name Return Code Description
OK 0 OK
UNKNOWN 1 Unknown error
NOMEM 2 No memory
INVAL 3 Invalid value
TIMEOUT 4 Operation timeout
NOENT 5 No entity
BADSTATE 6 Current state disallows command
MSGSIZE 7 Response too large
NOTSUP 8 Command not supported
Links:
https://github.com/apache/mynewt-mcumgr/blob/master/mgmt/include/mgmt/mgmt.h mcumgr error codes
Device Firmware Upgrade
Update:
Multi core update supported since NCS 1.7. Additional param "image" was added to IMAGE UPLOAD and IMAGE ERASE, where 0 is app
core (default) and 1 is net core. The change was mainly to support Thingy:53 and other nRF5340 devices. Each image needs to be sent
separately, than the test/command need to be sent for each image, and only than the device needs to be reset. Bin files for different cores are
placed in a ZIP file with manifest.json describing which file should be sent with which image parameter.
One of the features of MCU MGR is DFU. A signed image (.bin or .img) file must be created before starting DFU, using the proper certificate.
Signing adds the firmware hash and signature to a header before the firmware. Also, a MAGIC value is added that must match to one on the end
device.
DFU uses commands from 2 Group IDs: OS (0) and IMAGE (1).
Name Group ID Command ID Operation Parameters
Validate (list image slots) IMAGE (1) STATE (0) READ
Upload image IMAGE (1) UPLOAD (1) WRITE All required
Test (test image with given hash) IMAGE (1) STATE (0) WRITE confirm = false
hash
Confirm (confirm image with given hash) IMAGE (1) STATE (0) WRITE confirm = true
hash
Verify (confirm current slot) IMAGE (1) STATE (0) WRITE confirm = true
(no hash)
Reset OS (0) RESET (5) WRITE
Modes
There are 3 modes that an image may be sent:
1. Test and Confirm
2. Test only
3. Confirm only
Test and Confirm
This mode should be used by default if the firmware supports text/confirm. The client should validate the current slot information, send the new
image, send Test command, reset the end device, reconnect to the new image and send Verify command.
Test only
This mode allows only to test the image. It will not be confirmed and will be reverted to the original one after next reset. The client should validate,
send image, send Test command and reset.
Confirm only
This mode should be used by default if the firmware confirms itself and does not support test. E.g. multi core update in NCS 1.7 does not allow to
revert net-core update, so this is the only supported mode for multi-core update.
This may also be used if the new image does not support Bluetooth (reconnecting would not be possible), or we are sure it will work. Client
should validate, send image, send Confirm command and reset.
Algorithm
1. Read hash of the firmware to be sent.
2. Ensure the MTU is set to highest possible value.
3. Send Verify command to receive current slot information.
1. Check if slot 0's hash matches with the hash. If so, based on the mode and the image properties it could be Verified or DFU is
complete.
2. Check if slot 1's (if there is such) hash matches with the hash. If so, send Test or Confirm command, based on the mode.
4. Send the new image using Upload command. Many commands will have to be sent. As a response to each one the end device will
confirm the offset.
5. Send Test or Confirm command, based on the mode.
6. Send Reset command.
7. If Test and Confirm mode was used, reconnect to the device. Otherwise upload is complete.
8. Send Verify command. This will confirm the image in slot 0. If the test succeeded, and images were successfully swapped on reset, this
will confirm the tested image. Otherwise it will confirm already confirmed image and the invalid one (e.g. signed with a wrong certificate)
has been removed and slot 1 is empty.
9. Based on the result - it's either success, or some failure. Device may be test again to apply changes, but it's already running the proper
image.
Mobile clients
iOS MCUMGR Library and Sample app may be found here: https://github.com/JuulLabs-OSS/mcumgr-ios https://github.com
/NordicSemiconductor/IOS-nRF-Connect-Device-Manager
Android MCUMGR Library and Sample app may be found here: https://github.com/JuulLabs-OSS/mcumgr-android https://github.com
/NordicSemiconductor/Android-nRF-Connect-Device-Manager