Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Disconnected Device Debugging with Microvisor


Battery operated devices commonly disable wireless and cellular networks to save power. Mains-powered devices may be configured to continue operating even when connectivity has been lost due to circumstances beyond the device's control. With no network connectivity, logging is a challenge.

In order to add visibility to the state of your product during periods of unexpected connectivity loss, you can employ one of the many digital communication protocols available to your device's microcontroller, in particular UART. This standard bus is a great choice for its ease of use and wide availability.

By using a UART for offline debugging you can send the same messages you would via Microvisor's mvServerLog() system call to a serial port with just a few simple hardware modifications. We'll describe below how you can take advantage of this technique.


Tools

tools page anchor

You'll need a few extra tools in order to use UART for debugging. These tools comprise both software and hardware but are either low cost or free.

We recommend using a TTL UART-to-USB (COM port emulator) cable to view debug messages on your workstation. Connect this cable to pins (or test points) on the DUT.

You will also need terminal software and to modify the device code to output debug messages over serial. We have collected the information you need to source components and software.

FTDI cable

ftdi-cable page anchor

We recommend FTDI cables because they are highly available and relatively inexpensive. They are available with either a female 0.1-inch socket or bare wire termination, so select one on the basis of your application. They are available in 3.3V and 5V signal levels, and it is important that you always use the version that matches your microcontroller's GPIO logic high voltage.

A typical FTDI serial cable.

You will need to install the FTDI drivers for your OS. Ubuntu 20.0.4 comes with FTDI drivers built in, as does macOS (10.9 and above). Windows users will need to install VCP (Virtual COM Port) drivers, which are available from FTDI(link takes you to an external page).

There are many options available depending on your OS. Linux and macOS both come with Terminal utilities; these can be used to run a command-line utility called Minicom which lets you send and receive information via UART. Install Minicom using your OS' package manager.

  • Linux sudo apt install minicom
  • macOS brew install minicom

Windows users should download Simon Tatham's PuTTY(link takes you to an external page), which combines the role of terminal and Minicom.

(information)

Info

Minicom installation on macOS requires the Homebrew package manager. Install it from its website(link takes you to an external page) if you have not done so already.


To demonstrate UART connectivity, we'll use the Microvisor Nucleo Development Board (NDB). Microvisor makes four full-power UART buses — USART1, USART2, USART3, and UART4 — available to the application. We will use USART2 in the code below, but you should choose your own bus based on your device's pin availability.

USART2 connects through GPIO pins PA2 and PD5 (TX), and PA3 and PD6 (RX). For the sample code, we've chosen pins PD5 and PD6. In fact, we only need PD5, as we'll configure the UART for TX only.

Connect your FTDI cable to one of your computer's USB ports, and its RX wire (usually colored yellow) to PD5, which is pin 41 of block CN11 on the NDB. This is the GPIO header alongside the SIM card slot. Connect the FTDI's GND wire to pin 49 of CN11.

Microvisor pins 49 and 41, left to right, used for the sample UART connection.

Be sure to set the serial terminal connection to use the FTDI cable and match the following settings:

  • Baud Rate User choice, e.g., 115,200
  • Data Bits/Word Length 8
  • Parity None
  • Stop Bits 1

On your computer:

LinuxmacOSWindows 10

Open a terminal. The device file should be /dev/ttyACM0. You may need to ensure you have access to the serial port: on most distributions this can be done by adding your user account to the dialout user group.

Run minicom -o -D /dev/ttyACM0


The following sample can be used to add serial logging to your application. It leverages USART2 via pin PD5. The bus is set to TX only and an '8N1' configuration. You can alter these choices by editing the functions UART_init() and HAL_UART_MspInit(). In fact, the code demonstrates how to set up a standard UART connection using the STM32U585 HAL library: configure the UART, set the appropriate clocks, and configure the GPIO pins supporting the bus.

In your project, create the files uart_logging.h and uart_logging.c, and add the following code to the latter:


_105
#include "uart_logging.h"
_105
_105
UART_HandleTypeDef uart;
_105
_105
/**
_105
* @brief Configure STM32U585 UART1.
_105
*/
_105
void UART_init() {
_105
uart.Instance = USART2;
_105
uart.Init.BaudRate = 115200; // Match your chosen speed
_105
uart.Init.WordLength = UART_WORDLENGTH_8B; // 8
_105
uart.Init.StopBits = UART_STOPBITS_1; // N
_105
uart.Init.Parity = UART_PARITY_NONE; // 1
_105
uart.Init.Mode = UART_MODE_TX; // TX only mode
_105
uart.Init.HwFlowCtl = UART_HWCONTROL_NONE; // No CTS/RTS
_105
_105
// Initialize the UART
_105
if (HAL_UART_Init(&uart) != HAL_OK) {
_105
// Log error
_105
return;
_105
}
_105
}
_105
_105
/**
_105
* @brief HAL-called function to configure UART.
_105
*
_105
* @param uart: A HAL UART_HandleTypeDef pointer to the UART instance.
_105
*/
_105
void HAL_UART_MspInit(UART_HandleTypeDef *uart) {
_105
// This SDK-named function is called by HAL_UART_Init()
_105
_105
// Configure U5 peripheral clock
_105
RCC_PeriphCLKInitTypeDef PeriphClkInit = { 0 };
_105
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
_105
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
_105
_105
// Initialize U5 peripheral clock
_105
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
_105
// Log error
_105
return;
_105
}
_105
_105
// Enable the UART GPIO interface clock
_105
__HAL_RCC_GPIOD_CLK_ENABLE();
_105
_105
// Configure the GPIO pins for UART
_105
// Pin PD5 - TX
_105
GPIO_InitTypeDef gpioConfig = { 0 };
_105
gpioConfig.Pin = GPIO_PIN_5; // TX pin
_105
gpioConfig.Mode = GPIO_MODE_AF_PP; // Pin's alt function with pull...
_105
gpioConfig.Pull = GPIO_NOPULL; // ...but don't apply a pull
_105
gpioConfig.Speed = GPIO_SPEED_FREQ_HIGH;
_105
gpioConfig.Alternate = GPIO_AF7_USART2; // Select the alt function
_105
_105
// Initialize the pins with the setup data
_105
HAL_GPIO_Init(GPIOD, &gpioConfig);
_105
_105
// Enable the UART clock
_105
__HAL_RCC_USART2_CLK_ENABLE();
_105
}
_105
_105
/**
_105
* @brief Issue any log message via serial logging.
_105
*
_105
* @param format_string Message string with optional formatting
_105
* @param ... Arbitrary number of additional args
_105
*/
_105
void uartlog(char* format_string, ...) {
_105
char buffer[1024] = {0};
_105
va_list args;
_105
va_start(args, format_string);
_105
_105
// Add a timestamp
_105
char timestamp[64] = {0};
_105
uint64_t usec = 0;
_105
time_t sec = 0;
_105
time_t msec = 0;
_105
enum MvStatus status = mvGetWallTime(&usec);
_105
if (status == MV_STATUS_OKAY) {
_105
// Get the second and millisecond times
_105
sec = (time_t)usec / 1000000;
_105
msec = (time_t)usec / 1000;
_105
}
_105
_105
// Write time string as "2022-05-10 13:30:58.XXX "
_105
strftime(timestamp, 64, "%F %T.XXX ", gmtime(&sec));
_105
_105
// Insert the millisecond time over the XXX
_105
sprintf(&timestamp[20], "%03u ", (unsigned)(msec % 1000));
_105
_105
// Write the timestamp to the message
_105
strcpy(buffer, timestamp);
_105
size_t len = strlen(timestamp);
_105
_105
// Write the formatted text to the message
_105
vsnprintf(&buffer[len], 1016, format_string, args);
_105
_105
// Add RETURN and NEWLINE to the message and output to UART
_105
sprintf(&buffer[strlen(buffer)], "\r\n");
_105
HAL_UART_Transmit(&uart,
_105
(const uint8_t*)buffer,
_105
(uint16_t)strlen(buffer),
_105
100);
_105
va_end(args);
_105
}

Add a declaration for the first and last function to the header file:


_10
void UART_init();
_10
void uartlog(bool is_err, char* format_string, ...);

Make sure the files are included in your project's build configuration, and that you call UART_init() from your main(). Build and deploy a new version of your application.

Now update your code to call uartlog() alongside mvServerLog(). The function takes a format string and zero or more arguments to be interpolated into it. The message passed into the call will be UTC-timestamped and issued using Microvisor application logging and over UART. If the device is connected, you can view both streams in parallel, or you can view solely the UART output if the device is disconnected.

Debugging messages viewed in a local terminal.
(information)

Info

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.


Rate this page: