---
title: "Wendy Lite StdIO"
description: "On Wendy Lite, a WASM app can print data to the standard output. When developed in Swift, the app typically uses print() to do so."
---

# Wendy Lite StdIO

On Wendy Lite, a WASM app can print data to the standard output. When developed in Swift, the app typically uses `print()` to do so.

A native app can use `printf()` or any C API related to `stdout`. It can also use `write()` on `STDOUT_FILENO`. And finally, it can also use the ESP-IDF log routines, named `ESP_LOGx()`.

In all these cases, the output goes to these destinations:

* UART: Even if slow and archaic, this is the primary destination. Bootloader messages, and any message related to a runtime fatal error (assertions, crashes, ...), are output only on the UART interface.
* USB-Serial-JTAG (USJ): In the modern era, we always use USB to connect the computer to the device. This allows flashing the firmware, running the debugger and displaying logs.
* WendyCom connection: when the device is controlled remotely via the WendyCom protocol, the client can attach to the device and collect all outputs.

## Implementation

* `ESP_LOGx()` calls are redirected to stdout.
* `stdout` is a stdlib FILE opened on `/dev/console`.
* `/dev/console` is managed by the `wendy_stdio` component. It implements the VFS which redirects the output to the UART, to the USJ and to any additional subscriber.
* `wendy_com_stdio` subscribes to `wendy_stdio` and stores the output in a ring buffer, making the collected data available to the WendyCom client.

There is one caveat: when the output data is collected by the WendyCom client, `wendy_com` is in charge of draining the ring buffer. `wendy_com` runs in the com thread. Therefore, the com thread must never block and, in particular, it must never block by writing logs when the ring buffer is full. This would result in a deadlock.

To mitigate this problem, we adopted two rules:

1. The com thread must never write to stdout or stderr. Note that these FILEs have their own locks, which makes it impossible to prevent com thread deadlocks other than by avoiding the com thread writing to them in the first place.
2. Logs generated by the com thread by calling `ESP_LOGx()` go through a special path that never blocks.

When invoked in the com thread, `ESP_LOGx()` works in this way:

1. `wendy_com_stdio` subscribes to the `ESP_LOG` mechanism and intercepts the logs coming from the com thread.
2. `wendy_com_stdio` puts the data into the ring buffer, in non-blocking mode.
3. `wendy_com_stdio` delivers the same data to any subscriber.
4. `Wendy_USJ` subscribes to `wendy_com_stdio`, gets the com thread logs from it and prints them on the USJ output.
