Twilio Breakout SDK for Arduino
This page documents how to get started using the Breakout SDK and what it provides. Today, the Breakout SDK is built for the STM32F405RG MCU and U-Blox Sara-N410 cellular module in mind. This specific Developer Board was provided in Twilio's Alfa Developer Kit and distributed to SIGNAL 2018 attendees, and it came with Grove sensors – humidity, light, and ultrasonic.
Setting up your development environment
- Visit the Arduino IDE setup guide to learn how to set up your development environment.
- Visit the Alfa Developer Kit Quickstart to learn how to register and activate your Narrowband SIM in the Programmable Wireless Console.
Breakout SDK
Breakout SDK enables developers to exchange data between low-powered devices and IoT cloud services. Breakout makes it easy to adopt low power optimizations provided by the Narrowband network and cellular modem hardware.
Retrieve PSK from Console and assign PSK in client
A pre shared key (PSK) is required to be retrieved from the Programmable Wireless Console Narrowband SIM Resource. Your Narrowband SIM PSK will need to be copied into your application code.
PSKs are retrieved and set in hexadecimal and are unique per SIM.
static const char * psk_key = "00112233445566778899aabbccddeeff";
Your PSK can be found by navigating to the SIMs section of the Console, click on your Narrowband SIM, Click the Breakout SDK tab at the top. There you will see your
ICCID
and yourPSK
.
Retrieve the Breakout instance
Obtain an instance to the Breakout client singleton.
- @returns breakout client instance
Breakout *breakout = &Breakout::getInstance();
Set log level
Owl log provides robust output and color coding to Serial output. Log verbosity from most critical to most verbose:
- errors
L_ERR
- warnings
L_WARN
- information
L_INFO
- debug
L_DBG
owl_log_set_level(L_DBG);
When logging, the additional L_CLI level ensure that the output will always be visible, no matter the set level.
Set PSK in Breakout
PSK value is 16 bytes in hex or 32 characters.
breakout->setPSKKey(psk_key);
Set Polling Interval
Sets the interval (seconds) at which to poll for new Commands from the server. The default is 0, which indicates no automatic querying of the server. Value is specified in seconds, values < 60 will result in no polling. If last polling was far away in the past, a poll will follow. Otherwise (e.g. change of interval), the timer for the next poll is simply set to last_polling_time + interval_seconds.
breakout->setPollingInterval(10 * 60);
Polling
Checks for updates from server at defined interval.
Why we poll for Commands
In the non-Narrowband world, Commands are pushed to the device over SMS. Today, in the Narrowband world, the device must ask the server if there are any Commands waiting rather than the server telling the device that Commands are available.
Instead of the server sending a Command directly to the device, the device must ask the server if there are any Commands available to be sent to the device. This is why we have polling. Polling checks the server for a new Commands at a predefined interval no less than 60 seconds.
Heartbeats
Heartbeats are sent from Breakout to Twilio:
- When the polling interval is fired
- When
checkForCommands()
is manually fired - When you send a Command
- When you receive a Command
Heartbeats are registered with Breakout service and visible under the Breakout SDK tab of your Narrowband SIM Resource in Console. Heartbeats are displayed in reverse chronological order. You will see the version of Breakout that your Developer Board is running and the date and time the Heartbeat was sent.
Set the device's purpose
Sets the "purpose" of the device. This defaults to "Dev-Kit" and is informational to the network for the purpose of your device's use. The maximum length for purpose string is 32 characters. Must be set before powering on the module or this call will return an error.
- @param
purpose
- your developer kit's purpose. - @returns
true
is setting purpose was successful,false
otherwise
setPurpose(char const *purpose);
Power on the cellular module
Powering the modem and starting up the SDK.
Returns true
if powered on, false
otherwise.
breakout->powerModuleOn();
Handle all modem events
This is the main loop(), which will run forever. Keep breakout->spin()
call such that the SDK will be able to handle modem events, incoming data, and trigger retransmissions.
void loop() {
// Add here the code for your application, but don't block
// Add in this loop calls to your own application functions.
// Don't block or sleep inside them.
your_application_example();
// The Breakout SDK checking things and doing the work
breakout->spin();
// The delay (sleep) here helps conserve power, hence it is advisable to keep it.
delay(50);
}
Sending and receiving Commands from the device
Types
commands_status_code_e
Enumeration for connection status updates:
COMMAND_STATUS_OK
- Returned if operation was successful
COMMAND_STATUS_ERROR
- Returned if operation failed
COMMAND_STATUS_BUFFER_TOO_SMALL
- Returned if provided buffer is too small for requested data
COMMAND_STATUS_NO_COMMAND_WAITING
- Returned if no command is available for reading
COMMAND_STATUS_COMMAND_TOO_LONG
- Returned if provided command is too long
command_receipt_code_e
Enumeration for command status results:
CONNECTION_STATUS_OFFLINE
- The device is not registered on the mobile network - offline.
CONNECTION_STATUS_NETWORK_REGISTRATION_DENIED
- The mobile network denied network registration - check SIM status - offline.
CONNECTION_STATUS_REGISTERED_NOT_CONNECTED
- The device is registered to the mobile network but not yet connected to the Twilio Commands SDK - offline.
CONNECTION_STATUS_REGISTERED_AND_CONNECTED
- The device is registered to the mobile network and connected to the Twilio Commands SDK - online.
Handler for Commands with receipt requests
Handler function signature for Commands with receipt requests. Used in sendTextCommandWithReceiptRequest()
and sendBinaryCommandWithReceiptRequest()
.
- @param
receipt_code
- the receipt request - @param
cb_parameter
- a generic pointer to application data, as passed to thesendCommandWithReceiptRequest()
typedef void (*BreakoutCommandReceiptCallback_f)(command_receipt_code_e receipt_code, void *cb_parameter);
Methods
Send a text Command without a receipt request
The Command to send to Twilio - max 140 characters.
- @param
cmd
- the command to send to Twilio - max 140 characters. - @returns
command_status_code_e
sendTextCommand(const char *buf);
Send a binary Command without a receipt request
The Command to send to Twilio - max 140 characters.
- @param
cmd
- the command to send to Twilio - max 140 characters. - @returns
command_status_code_e
sendBinaryCommand(const char *buf);
Send a text Command with a receipt request
The text Command to send to Twilio - max 140 characters.
- @param
buf
- the text command to send to Twilio - max 140 characters. - @param
callback
- command receipt callback. - @param
callback_parameter
- a generic pointer to application data. - @returns:
command_status_code_e
sendTextCommandWithReceiptRequest(const char *buf, BreakoutCommandReceiptCallback_f callback, void *callback_parameter);
Send a binary Command with a receipt request
The binary Command to send to Twilio - max 140 characters.
- @param
buf
- the text command to send to Twilio - max 140 characters. - @param
callback
- command receipt callback. - @param
callback_parameter
- a generic pointer to application data. - @returns:
command_status_code_e
sendBinaryCommandWithReceiptRequest(const char *buf, BreakoutCommandReceiptCallback_f callback, void *callback_parameter);
Receive Commands
Pop a received Command, in case it was received locally (hasWaitingCommand() returns true
.
- @param
maxBufSize
- size of buffer being passed in. - @param
buf
- buffer to receive command into. - @param
bufSize
- output size of returned command in buf, will not exceed 141 bytes. - @param
isBinary
- output indicator if the command was received with Content-Format indicating text or binary. - @returns
command_status_code_e
receiveCommand(const size_t maxBufSize, char *buf, size_t *bufSize, bool *isBinary);
Check if Commands are waiting to be retrieved
Indicates the presence of a waiting command.
@returns true
if Commands waiting on server, false
otherwise.
hasWaitingCommand();
Manually check for Commands
Manually initiate a check for waiting Commands.
If setPollingInterval()
is set to a valid interval, this is automatically called at an interval. If both polling interval is enabled, the polling timer is reset on manual calls to this method.
- @returns
true
if the operation was successful.
Query hasWaitingCommands()
for results.
checkForCommands(bool isRetry = false);
Sending Commands to the SIM
Commands to the SIM are sent via an HTTP request to Twilio. Visit the Commands API documentation to learn more.
Create a text Command
curl -X POST https://wireless.twilio.com/v1/Commands \
--data-urlencode "Sim=MyNarrowbandSim" \
--data-urlencode "Command=wakeup" \
-u ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:your_auth_token
Create a binary Command
curl -X POST https://wireless.twilio.com/v1/Commands \
--data-urlencode "CommandMode=binary" \
--data-urlencode "Sim=MyNarrowbandSIM" \
--data-urlencode "Command=SGVsbG8sIE1hY2hpbmUh==" \
-u ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:your_auth_token
Receive Command from SIM to your servers
To receive a Command from a SIM 'mobile-originated', you should create or update an existing SIM instance with a CommandsCallbackUrl
property, and optionally a CommandsCallbackMethod
property (defaults to POST).
Limitations and Workarounds
- U-Blox data size limitation
- Problem: The U-Blox cellular module can receive up to 512 bytes of data in hex mode. Hence this is a limitation for UDP datagrams.
- Solution: partial solution would be to switch from the hex mode to binary mode. This shall double the amount of data received, yet it makes the code much more complex. So far 512 was a good upper limit, especially given the NB-IoT targets here, hence this wasn't explored.
- Arduino Seeed STM32F4 Boards Package - USART buffer limitation
- Problem: The Seeed STM32F4 Boards package, published through the Boards Manager system, has a limitation in the USART interface implementation which we're using to communicate with the modem. The default USART_RX_BUF_SIZE is set to 256 bytes. With overheads and hex mode, this reduces the maximum chunk of data which we can read from the modem quite significantly. The effect is that the modem data in the receive side is shifted over its beginning, such that only the last 255 bytes are actually received by OwlModem.
- Solution: see Update USART buffer below.
Update USART buffer
This is required for Wio STM32F4 devices not on 1.2.3.
- Locate
/libmaple/usart.h
- OSX:
/Users/{{UserNameHere}}/Library/Arduino15/packages/Seeeduino/hardware/Seeed_STM32F4/{{VersionHere}}/cores/arduino/libmaple/usart.h
- Linux:
~/.arduino15/packages/Seeeduino/hardware/Seeed_STM32F4/{{VersionHere}}/cores/arduino/libmaple/usart.h
- Windows:
C:/Users/{{UserNameHere}}/Documents/Arduino/hardware/Seeed_STM32F4/{{VersionHere}}/cores/arduino/libmaple/usart.h
- OSX:
- Update
USART_RX_BUF_SIZE
to1280
- Update
USART_TX_BUF_SIZE
to1280