Skip to contentSkip to navigationSkip to topbar
On this page

Microvisor Networking


(warning)

Microvisor Public Beta

Microvisor is in a pre-release phase and the information contained in this document is subject to change. Some features referenced below may not be fully available until Microvisor's General Availability (GA) release.

Microvisor owns all of the device's networking sub-systems which can be used to connect the device to the Internet. Microvisor makes these network sub-systems available to the application through the Microvisor System Calls to allow your code to transfer data to and from remote servers.

This guide will help you understand how this is achieved by walking you through a typical usage flow:

  • Establish a network connection.
  • Set up a data channel.
  • Send and receive data through the channel.
  • Close the channel.
  • End the network connection.

This guide focuses on establishing network connections and channels, and then tearing them down when they are no longer required: the top and bottom of the sequence listed above. The phase in the middle, that of using the channel to send and receive data, is covered in separate guides, one each for the supported protocols.

(warning)

Warning

Earlier versions of this guide made use of channel-oriented Microvisor System Calls that have since been deprecated. These calls are no longer referred to in this guide.


Using networking

using-networking page anchor

At a high level, your application asks Microvisor to provide it with a network connection. When it has done so, Microvisor passes the application a handle which uniquely identifies the connection. The application can use this handle to check the connection's status and to open two-way data pathways, called channels, that are hosted by the connection. The application might use a channel to request data from a cloud service and to read the response. When it has completed the interaction, the application closes the channel. If it no longer needs the network connection, it relinquishes its access.

Like the network connection, every channel is identified by its own handle. Each handle's value is a 32-bit unsigned integer that is unique for the lifetime of the resource to which it has been assigned.

Handles are used to specify all types of resource in application-Microvisor interactions. When you open a channel, you provide the handle of the network connection that will host it: you include the network connection handle in the channel configuration data. Microvisor will provide a channel handle in return: use this channel handle for all future work with the channel.

(warning)

Warning

Before using a handle, you should always check that it is not zero. Any handle with the value zero is invalid. All extant handles are zeroed when Microvisor boots, and specific handles are zeroed when the resource they refer to is relinquished or closed by the application through the appropriate system call. If you attempt to make use of an invalid handle, Microvisor will report it as an error.


When Microvisor starts up, it will attempt to establish a network connection. It will maintain this connection until your application takes ownership of the network state by requesting network connectivity. From this point, it is your application's responsibility to manage when the device is connected to the network and when it is not.

Microvisor will take advantage of the application-managed connection to check if application or system updates have been staged for download. If the application intentionally goes offline, or if it doesn't reconnect after an outage, Microvisor will autonomously connect when it needs to make one of its periodic check-ins with the Microvisor cloud. It will stay connected for as short a time as possible.

Microvisor will also connect if the application crashes. This puts ownership of network state back in the hands of Microvisor until your code once again makes a network request.

A special case is remote debugging. In this case, Microvisor maintains a connection for debugging even if the application disconnects.

The System Call mvGetNetworkReasons() provides a means for your application to determine the state of network ownership. It can be called at any time, even if your application hasn't yet requested access to the network. The call writes out a bitfield of flags that together indicate current networking status. For example, examining the bitfield after the application has closed the network connection might reveal that Microvisor nonetheless has a connection in place because a remote debugger is attached.

You can view all the flags that may be set in the bitfield in the mvGetNetworkReasons() documentation.

Application logging

application-logging page anchor

Microvisor's application logging system implicitly makes use of any available connection to relay posted messages to the Microvisor cloud, from when they will be streamed to any remote computer that's listening for them. This means that it's not necessary for your application to request network connectivity solely to have posted log messages relayed. However, if you do request network access for other reasons, and you close the connection, messages will not be relayed.

Log messages posted while the device is offline — intentionally or unintentionally — are buffered so long as their is sufficient space in the buffer for them. Buffered messages will be sent to the cloud as soon as connectivity is restored by the current owner.


Requesting network connectivity

requesting-network-connectivity page anchor

To request a network connection, you application calls mvRequestNetwork(). This has these parameters:

  • A pointer to a data structure which allows you to specify how Microvisor will notify your application about subsequent network operations.
  • A pointer to application-accessible memory into which Microvisor will write the network connection's handle.

Microvisor will now bring up the network connection, powering up the hardware and making a connection to the Internet if these are not already in place. Once this process is complete, Microvisor will signal network availability to the application by dispatching an MV_EVENTTYPE_NETWORKSTATUSCHANGED notification.

(information)

Info

For more information on when and how Microvisor issues notifications to the application, and how the application can register its interest in certain notifications, please see Microvisor Notifications.

With a network connection in place, the application is ready to open channels and use them to transfer data.

(information)

Info

Like all Microvisor system calls, mvRequestNetwork() immediately returns a status value indicating whether Microvisor is able to proceed with the request. Only continue if Microvisor returns the value MV_STATUS_OKAY.

You can check on the status of a network connection at any time by calling mvGetNetworkStatus() and passing both the handle of the network you're interested in and the address of memory into which Microvisor will write a connection status code which will be one of the following:

StatusDescription
MV_NETWORKSTATUS_DELIBERATELYOFFLINEThe device is not connected
MV_NETWORKSTATUS_CONNECTEDThe device is connected
MV_NETWORKSTATUS_CONNECTINGThe device is connecting to the Internet

First, you need to establish the notification center to which network notifications will be posted. This center will then be used to configure the network connection itself. The code also shows how to set up stores for the various types of handle that will be used, and for notifications.

1
// Central store for Microvisor resource handles used in this code.
2
struct {
3
MvNotificationHandle notification;
4
MvNetworkHandle network;
5
MvChannelHandle channel;
6
} net_handles = { 0, 0, 0 };
7
8
// Central store for notification records. Holds one record at
9
// a time -- each record is 16 bytes in size.
10
static volatile struct MvNotification net_notification_buffer[16];
11
12
// Clear the notification store
13
memset((void *)net_notification_buffer, 0xff, sizeof(net_notification_buffer));
14
15
// Configure a notification center for network-centric notifications
16
static struct MvNotificationSetup net_notification_config = {
17
.irq = TIM1_BRK_IRQn,
18
.buffer = (struct MvNotification *)net_notification_buffer,
19
.buffer_size = sizeof(net_notification_buffer)
20
};
21
22
// Ask Microvisor to establish the notification center
23
// and confirm that it has accepted the request
24
enum MvStatus status = mvSetupNotifications(&net_notification_config,
25
&net_handles.notification);
26
assert(status == MV_STATUS_OKAY);
27
28
// Start the notification IRQ
29
NVIC_ClearPendingIRQ(TIM1_BRK_IRQn);
30
NVIC_EnableIRQ(TIM1_BRK_IRQn);

Here is the code you would write to open a network connection:

1
// Configure the network connection request
2
struct MvRequestNetworkParams network_config = {
3
.version = 1,
4
.v1 = {
5
.notification_handle = net_handles.notification,
6
.notification_tag = USER_TAG_LOGGING_REQUEST_NETWORK,
7
}
8
};
9
10
// Ask Microvisor to establish the network connection
11
// and confirm that it has accepted the request
12
enum MvStatus status = mvRequestNetwork(&network_config,
13
&net_handles.network);
14
assert(status == MV_STATUS_OKAY);

However, because the connection is established asynchronously, it's important to check network status before proceeding to open the channel — which will fail if the connection is not yet ready:

1
// The network connection is established by Microvisor asynchronously,
2
// so we wait for it to come up before opening the data channel -- which
3
// would fail otherwise
4
enum MvNetworkStatus net_status;
5
while (1) {
6
// Request the status of the network connection, identified by its handle.
7
// If we're good to continue, break out of the loop...
8
if (mvGetNetworkStatus(net_handles.network, &net_status) == MV_STATUS_OKAY &&
9
net_status == MV_NETWORKSTATUS_CONNECTED) {
10
break;
11
}
12
13
// ... or wait a short period before retrying
14
for (volatile unsigned i = 0; i < 50000; ++i) {
15
// No op
16
__asm("nop");
17
}
18
}

Recall that Microvisor system calls return immediately with a value — status in the code above — that indicates whether the request was accepted or rejected, not whether the request itself succeeded or failed.

General connection state checks

general-connection-state-checks page anchor

A call to mvGetNetworkStatus() can be used at any time to determine the state of the device's connection. This code assumes your network connection's handle is stored in a variable called network_handle.

1
// Check connection state
2
bool is_connected = false;
3
if (net_handles.network != 0) {
4
enum MvNetworkStatus net_state = MV_NETWORKSTATUS_DELIBERATELYOFFLINE;
5
uint32_t status = mvGetNetworkStatus(net_handles.network, &net_state);
6
if (status == MV_STATUS_OKAY) {
7
is_connected = (net_state == MV_NETWORKSTATUS_CONNECTED);
8
}
9
}

To open a channel, call mvOpenChannel(). This function takes a data structure that's used to configure the channel, and a pointer to memory where Microvisor will write the channel's handle.

The configuration data comprises:

  • Pointers to the channel's send and receive buffers created by the application.
  • The sizes of those buffers.
  • The channel type.
  • The handle of the network connection that will host the channel.

The network connection that is hosting the channel must be connected to the Internet or the attempt to open the channel will fail. If connectivity is subsequently lost, any open channels on the network will be closed automatically, and your application will receive an EVENT_NETWORK_STATUS_CHANGED notification. This should trigger a call to mvGetNetworkStatus().

The channel type value tells Microvisor how the channel is to be used: the protocol it should use to send and receive information. At this time, only HTTPS, MQTT, and Configuration - specified by the constants MV_CHANNELTYPE_HTTP, MV_CHANNELTYPE_MQTT, and MV_CHANNELTYPE_CONFIGFETCH, respectively — are supported, but we may add support for other protocols in due course.

The channel configuration structure includes an endpoint property and the related endpoint_len. This is no longer used, but you will need to provide the data: set the length to zero, and pass in a pointer to a dummy variable.

Send and receive buffers

send-and-receive-buffers page anchor

The application is responsible for allocating its own channel send and receive buffers. However, once Microvisor has opened a channel, it isn't possible to change the sizes or addresses of the channel's buffers. If you need to do so, you must close the channel and open a new one.

Each buffer is treated by Microvisor as circular. It will maintain a pointer and move it forward as data is written and read. When the pointer reaches the end of the buffer, it continues from at the start of the buffer once more. As data leaves the send buffer, space is made for further data to be sent. Microvisor keeps track of the available space for you.

Buffers must be sized in multiples of 512 bytes and their addresses aligned to 512-byte boundaries.

Having already requested a network connection and ensured that it is up before proceeding, we can now open the channel:

Set up the channel's send and receive buffers with appropriate sizes and alignment:

1
// Set up the HTTP channel's multi-use send and receive buffers
2
static volatile uint8_t http_channel_rx_buffer[RX_BUFFER_SIZE_B] __attribute__((aligned(512)));
3
static volatile uint8_t http_channel_tx_buffer[TX_BUFFER_SIZE_B] __attribute__((aligned(512)));

Now add those buffers to the channel definition:

1
struct MvOpenChannelParams channel_config = {
2
.version = 1,
3
.v1 = {
4
.notification_handle = http_handles.notification,
5
.notification_tag = USER_TAG_HTTP_OPEN_CHANNEL,
6
.network_handle = http_handles.network,
7
.receive_buffer = (uint8_t*)http_channel_rx_buffer,
8
.receive_buffer_len = sizeof(http_channel_rx_buffer),
9
.send_buffer = (uint8_t*)http_channel_tx_buffer,
10
.send_buffer_len = sizeof(http_channel_tx_buffer),
11
.channel_type = MV_CHANNELTYPE_HTTP,
12
.endpoint = {
13
.data = (uint8_t*)"",
14
.length = 0
15
}
16
}
17
};

Finally, request the channel be opened:

1
// Ask Microvisor to open the channel
2
// and confirm that it has accepted the request
3
enum MvStatus status = mvOpenChannel(&channel_config, &net_handles.channel);
4
if (status == MV_STATUS_OKAY) {
5
server_log("Channel handle: %lu", (uint32_t)net_handles.channel);
6
} else {
7
server_error("Channel opening failed. Status: %i", status);
8
}

The functions server_log() and server_error() are functions which take a format string and zero or more value arguments, render the string, and issue it via mvServerLog(). You can view both functions' code in the HTTP demo repo(link takes you to an external page).

(information)

Info

To learn how to use the HTTP channel you have just established, please see How to Issue HTTP Requests under Microvisor .


When you have finished with a channel, call mvClosechannel() to release it. It will zero the channel handle to prevent the use of that handle again — doing so will cause Microvisor to issue an error.

Closing a channel is straightforward, but it's prudent to check that the closure request was successful. Your application might need to follow a different path if the response from mvCloseChannel() is not MV_STATUS_OKAY.

1
// If we have a valid channel handle -- ie. it is non-zero --
2
// then ask Microvisor to close it and confirm acceptance of
3
// the closure request.
4
if (net_handles.channel != 0) {
5
enum MvStatusstatus = mvCloseChannel(&net_handles.channel);
6
assert(status == MV_STATUS_OKAY);
7
}
8
9
// Confirm the channel handle has been invalidated by Microvisor
10
assert(net_handles.channel == 0);

Unexpected channel closures

unexpected-channel-closures page anchor

Devices can lose connectivity for a variety of reasons: they are in an area with marginal cellular coverage, or the local network to which they are connected has lost its Internet backhaul. Whatever the reason for the loss of connectivity, it will cause Microvisor to post an MV_EVENTTYPE_CHANNELNOTCONNECTED notification to every open channel. However, Microvisor does not close the channels. This is so that your application can read any data still in their receive buffers, though no new data will be added to them.

You can call mvReadchannel() and mvReadchannelComplete() to get data that was received before the loss of connectivity, and then mvClosechannel() to terminate the channel.

However, you cannot call mvWritechannel(). No data can be sent because there's no connection, so Microvisor will issue an error if you try to do so.


Close the network connection

close-the-network-connection page anchor

Any time that you have a network connection, you can relinquish your application's access to it by calling mvReleaseNetwork(). It has a single parameter: the network connection handle.

Releasing the network connection will cause it to be closed — but only if there are no other handles that reference it, and Microvisor is not making use of it to check for OS and application updates. If you release a network connection that is host to one or more open channels, those channels will each receive an MV_EVENTTYPE_CHANNELNOTCONNECTED notification as described above.

Closing the network is straightforward, but don't forget to remove the notification center assigned to the network if you no longer need it.

1
// If we have a valid network handle, then ask Microvisor to
2
// close the connection and confirm acceptance of the request.
3
if (net_handles.network != 0) {
4
status = mvReleaseNetwork(&net_handles.network);
5
assert(status == MV_STATUS_OKAY);
6
}
7
8
// Confirm the network handle has been invalidated by Microvisor
9
assert(net_handles.network == 0);
10
11
// If we have a valid notification center handle, then ask Microvisor
12
// to tear down the center and confirm acceptance of the request.
13
if (net_handles.notification != 0) {
14
status = mvCloseNotifications(&net_handles.notification);
15
assert(status == MV_STATUS_OKAY);
16
}
17
18
// Confirm the notification center handle has been invalidated
19
assert(net_handles.notification == 0);
(information)

Microvisor Help and Support

We welcome all inquiries you may have about Microvisor and its implementation, and any support questions that arise once you've begun developing with Microvisor. Please submit your queries via a KORE Wireless ticket: log in to the Kore console(link takes you to an external page) and click the Contact Support button in the left-hand navbar.

Need some help?

Terms of service

Copyright © 2024 Twilio Inc.