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:
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.
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.
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.
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.
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.
To request a network connection, you application calls mvRequestNetwork()
. This has these parameters:
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.
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.
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:
Status | Description |
---|---|
MV_NETWORKSTATUS_DELIBERATELYOFFLINE | The device is not connected |
MV_NETWORKSTATUS_CONNECTED | The device is connected |
MV_NETWORKSTATUS_CONNECTING | The 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.2struct {3MvNotificationHandle notification;4MvNetworkHandle network;5MvChannelHandle channel;6} net_handles = { 0, 0, 0 };78// Central store for notification records. Holds one record at9// a time -- each record is 16 bytes in size.10static volatile struct MvNotification net_notification_buffer[16];1112// Clear the notification store13memset((void *)net_notification_buffer, 0xff, sizeof(net_notification_buffer));1415// Configure a notification center for network-centric notifications16static 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};2122// Ask Microvisor to establish the notification center23// and confirm that it has accepted the request24enum MvStatus status = mvSetupNotifications(&net_notification_config,25&net_handles.notification);26assert(status == MV_STATUS_OKAY);2728// Start the notification IRQ29NVIC_ClearPendingIRQ(TIM1_BRK_IRQn);30NVIC_EnableIRQ(TIM1_BRK_IRQn);
Here is the code you would write to open a network connection:
1// Configure the network connection request2struct MvRequestNetworkParams network_config = {3.version = 1,4.v1 = {5.notification_handle = net_handles.notification,6.notification_tag = USER_TAG_LOGGING_REQUEST_NETWORK,7}8};910// Ask Microvisor to establish the network connection11// and confirm that it has accepted the request12enum MvStatus status = mvRequestNetwork(&network_config,13&net_handles.network);14assert(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 -- which3// would fail otherwise4enum MvNetworkStatus net_status;5while (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...8if (mvGetNetworkStatus(net_handles.network, &net_status) == MV_STATUS_OKAY &&9net_status == MV_NETWORKSTATUS_CONNECTED) {10break;11}1213// ... or wait a short period before retrying14for (volatile unsigned i = 0; i < 50000; ++i) {15// No op16__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.
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 state2bool is_connected = false;3if (net_handles.network != 0) {4enum MvNetworkStatus net_state = MV_NETWORKSTATUS_DELIBERATELYOFFLINE;5uint32_t status = mvGetNetworkStatus(net_handles.network, &net_state);6if (status == MV_STATUS_OKAY) {7is_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:
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.
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 buffers2static volatile uint8_t http_channel_rx_buffer[RX_BUFFER_SIZE_B] __attribute__((aligned(512)));3static volatile uint8_t http_channel_tx_buffer[TX_BUFFER_SIZE_B] __attribute__((aligned(512)));
Now add those buffers to the channel definition:
1struct 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 = 015}16}17};
Finally, request the channel be opened:
1// Ask Microvisor to open the channel2// and confirm that it has accepted the request3enum MvStatus status = mvOpenChannel(&channel_config, &net_handles.channel);4if (status == MV_STATUS_OKAY) {5server_log("Channel handle: %lu", (uint32_t)net_handles.channel);6} else {7server_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.
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 of3// the closure request.4if (net_handles.channel != 0) {5enum MvStatusstatus = mvCloseChannel(&net_handles.channel);6assert(status == MV_STATUS_OKAY);7}89// Confirm the channel handle has been invalidated by Microvisor10assert(net_handles.channel == 0);
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.
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 to2// close the connection and confirm acceptance of the request.3if (net_handles.network != 0) {4status = mvReleaseNetwork(&net_handles.network);5assert(status == MV_STATUS_OKAY);6}78// Confirm the network handle has been invalidated by Microvisor9assert(net_handles.network == 0);1011// If we have a valid notification center handle, then ask Microvisor12// to tear down the center and confirm acceptance of the request.13if (net_handles.notification != 0) {14status = mvCloseNotifications(&net_handles.notification);15assert(status == MV_STATUS_OKAY);16}1718// Confirm the notification center handle has been invalidated19assert(net_handles.notification == 0);
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 and click the Contact Support button in the left-hand navbar.