ALXV Labs Byte90
Byte90
The Byte90 is a development board based on the ESP32-S3 module, featuring a USB-C connector, a color 128x128 OLED, a button, and an ADXL345 accelerometer, all in a little housing that makes it look like a retro computer (e.g. C64, ZX Spectrum).
See the Byte90 product page for more information.
The espp::Byte90 component provides a singleton hardware abstraction for initializing the various subsystems.
API Reference
Header File
Classes
-
class Byte90 : public espp::BaseComponent
The Byte90 class provides an interface to the Byte90 ESP32-S3 development board.
The class provides access to the following features:
The class is a singleton and can be accessed using the get() method.
For more information, see https://github.com/alxv2016/Byte90-alxvlabs
Pin Reference Table:
XIAO Pin
GPIO
Function
Component
D0
GPIO1
RST
Display Reset
D1
GPIO2
INT
ADXL345 Interrupt
A3
GPIO4
INPUT
Button (with pull-up)
D4
GPIO5
SDA
ADXL345 I2C Data
D5
GPIO6
SCL
ADXL345 I2C Clock
D8
GPIO7
SCK
Display SPI Clock
D10
GPIO9
MOSI
Display SPI Data
D6
GPIO43
DC
Display Data/Command
D7
GPIO44
CS
Display Chip Select
Example
espp::Byte90 &byte90 = espp::Byte90::get(); byte90.set_log_level(espp::Logger::Verbosity::INFO); // initialize the accelerometer if (!byte90.initialize_accelerometer()) { logger.error("Failed to initialize accelerometer!"); } // initialize the LCD if (!byte90.initialize_lcd()) { logger.error("Failed to initialize LCD!"); return; } // set the pixel buffer to be 50 lines high static constexpr size_t pixel_buffer_size = byte90.lcd_width() * 50; // initialize the LVGL display for the Byte90 if (!byte90.initialize_display(pixel_buffer_size)) { logger.error("Failed to initialize display!"); return; } // initialize the button, which we'll use to cycle the rotation of the display logger.info("Initializing the button"); auto on_button_pressed = [&](const auto &event) { if (event.active) { // increment the brightness by 10%, looping back to 0% after 100% auto brightness = byte90.brightness(); brightness = std::fmod(brightness + 10.0f, 100.0f); logger.info("Setting brightness to {:.0f}%", brightness); byte90.brightness(brightness); // lock the display mutex std::lock_guard<std::recursive_mutex> lock(lvgl_mutex); static auto rotation = LV_DISPLAY_ROTATION_0; rotation = static_cast<lv_display_rotation_t>((static_cast<int>(rotation) + 1) % 4); lv_display_t *disp = lv_disp_get_default(); lv_disp_set_rotation(disp, rotation); } }; byte90.initialize_button(on_button_pressed); // set the background color to black lv_obj_t *bg = lv_obj_create(lv_screen_active()); lv_obj_set_size(bg, byte90.lcd_width(), byte90.lcd_height()); lv_obj_set_style_bg_color(bg, lv_color_make(0, 0, 0), 0); // add text in the center of the screen lv_obj_t *label = lv_label_create(lv_screen_active()); lv_label_set_text(label, "Drawing circles\nto the screen."); lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0); // start a simple thread to do the lv_task_handler every 16ms espp::Task lv_task({.callback = [](std::mutex &m, std::condition_variable &cv) -> bool { { std::lock_guard<std::recursive_mutex> lock(lvgl_mutex); lv_task_handler(); } std::unique_lock<std::mutex> lock(m); cv.wait_for(lock, 16ms); return false; }, .task_config = { .name = "lv_task", }}); lv_task.start(); // set the display brightness to be 75% byte90.brightness(75.0f); while (true) { auto start = esp_timer_get_time(); // if there are 10 circles on the screen, clear them static constexpr int max_circles = 10; if (circles.size() >= max_circles) { // lock the lvgl mutex std::lock_guard<std::recursive_mutex> lock(lvgl_mutex); clear_circles(); } else { // draw a circle of circles on the screen (just draw the next circle) static constexpr int middle_x = byte90.lcd_width() / 2; static constexpr int middle_y = byte90.lcd_height() / 2; static constexpr int radius = 30; float angle = circles.size() * 2.0f * M_PI / max_circles; int x = middle_x + radius * cos(angle); int y = middle_y + radius * sin(angle); // lock the lvgl mutex std::lock_guard<std::recursive_mutex> lock(lvgl_mutex); draw_circle(x, y, 5); } auto end = esp_timer_get_time(); auto elapsed = end - start; std::this_thread::sleep_for(100ms - std::chrono::microseconds(elapsed)); }
Public Types
-
using button_callback_t = espp::Interrupt::event_callback_fn
Alias for the button callback function.
-
using AccelerometerData = Accelerometer::Data
Alias for the Accelerometer data type.
-
using accel_callback_t = std::function<void(const std::chrono::high_resolution_clock::time_point ×tamp, const std::vector<AccelerometerData> &data)>
Alias for an accelerometer interrupt callback.
Public Functions
-
inline I2c &internal_i2c()
Get a reference to the internal I2C bus
Note
The internal I2C bus is used for the accelerometer
- Returns:
A reference to the internal I2C bus
-
inline espp::Interrupt &interrupts()
Get a reference to the interrupts
- Returns:
A reference to the interrupts
-
bool initialize_button(const button_callback_t &callback = nullptr)
Initialize the button
- Parameters:
callback – The callback function to call when the button is pressed
- Returns:
true if the button was successfully initialized, false otherwise
-
bool button_state() const
Get the button state
- Returns:
The button state (true = button pressed, false = button released)
-
bool initialize_accelerometer(const accel_callback_t &callback = nullptr)
Initialize the accelerometer
Note
This will configure the accelerometer interrupt pin which will automatically call the accel callback function when the accel interrupt occurs
- Parameters:
callback – The callback function to call when the accelerometer interrupt occurs
- Returns:
true if the accelerometer was successfully initialized, false otherwise
-
inline std::shared_ptr<Accelerometer> accelerometer() const
Get the Accelerometer shared pointer
- Returns:
A shared pointer to the Accelerometer instance
-
inline std::vector<AccelerometerData> accelerometer_data()
Get the most recent accelerometer data
- Returns:
The most recent accelerometer data
-
bool initialize_lcd()
Initialize the LCD (low level display driver)
- Returns:
true if the LCD was successfully initialized, false otherwise
-
bool initialize_display(size_t pixel_buffer_size)
Initialize the display (lvgl display driver)
Note
This will also allocate two full frame buffers in the SPIRAM
- Parameters:
pixel_buffer_size – The size of the pixel buffer
- Returns:
true if the display was successfully initialized, false otherwise
-
std::shared_ptr<Display<Pixel>> display() const
Get a shared pointer to the display
- Returns:
A shared pointer to the display
-
void brightness(float brightness)
Set the brightness of the backlight
- Parameters:
brightness – The brightness of the backlight as a percentage (0 - 100)
-
float brightness() const
Get the brightness of the backlight
- Returns:
The brightness of the backlight as a percentage (0 - 100)
-
Pixel *vram0() const
Get the VRAM 0 pointer (DMA memory used by LVGL)
Note
This is the memory used by LVGL for rendering
Note
This is null unless initialize_display() has been called
- Returns:
The VRAM 0 pointer
-
Pixel *vram1() const
Get the VRAM 1 pointer (DMA memory used by LVGL)
Note
This is the memory used by LVGL for rendering
Note
This is null unless initialize_display() has been called
- Returns:
The VRAM 1 pointer
-
uint8_t *frame_buffer0() const
Get the frame buffer 0 pointer
Note
This memory is designed to be used by the application developer and is provided as a convenience. It is not used by the display driver.
Note
This is null unless initialize_display() has been called
- Returns:
The frame buffer 0 pointer
-
uint8_t *frame_buffer1() const
Get the frame buffer 1 pointer
Note
This memory is designed to be used by the application developer and is provided as a convenience. It is not used by the display driver.
Note
This is null unless initialize_display() has been called
- Returns:
The frame buffer 1 pointer
-
void write_command(uint8_t command, std::span<const uint8_t> parameters, uint32_t user_data)
Write command and optional parameters to the LCD
Note
This method is designed to be used by the display driver
Note
This method queues the data to be written to the LCD, only blocking if there is an ongoing SPI transaction
- Parameters:
command – The command to write
parameters – The command parameters to write
user_data – User data to pass to the spi transaction callback
-
void write_lcd_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t *data)
Write a frame to the LCD
Note
This method queues the data to be written to the LCD, only blocking if there is an ongoing SPI transaction
- Parameters:
x – The x coordinate
y – The y coordinate
width – The width of the frame, in pixels
height – The height of the frame, in pixels
data – The data to write
-
void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data)
Write lines to the LCD
Note
This method queues the data to be written to the LCD, only blocking if there is an ongoing SPI transaction
- Parameters:
xs – The x start coordinate
ys – The y start coordinate
xe – The x end coordinate
ye – The y end coordinate
data – The data to write
user_data – User data to pass to the spi transaction callback
-
inline const std::string &get_name() const
Get the name of the component
Note
This is the tag of the logger
- Returns:
A const reference to the name of the component
-
inline void set_log_tag(const std::string_view &tag)
Set the tag for the logger
- Parameters:
tag – The tag to use for the logger
-
inline espp::Logger::Verbosity get_log_level() const
Get the log level for the logger
See also
See also
- Returns:
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- Parameters:
level – The verbosity level to use for the logger
-
inline void set_log_verbosity(espp::Logger::Verbosity level)
Set the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls set_log_level
- Parameters:
level – The verbosity level to use for the logger
-
inline espp::Logger::Verbosity get_log_verbosity() const
Get the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls get_log_level
- Returns:
The verbosity level of the logger
-
inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)
Set the rate limit for the logger
See also
Note
Only calls to the logger that have _rate_limit suffix will be rate limited
- Parameters:
rate_limit – The rate limit to use for the logger
Public Static Functions
-
static inline Byte90 &get()
Access the singleton instance of the Byte90 class.
- Returns:
Reference to the singleton instance of the Byte90 class
-
static inline constexpr size_t lcd_width()
Get the width of the LCD in pixels
- Returns:
The width of the LCD in pixels
-
static inline constexpr size_t lcd_height()
Get the height of the LCD in pixels
- Returns:
The height of the LCD in pixels
-
static inline constexpr auto get_lcd_dc_gpio()
Get the GPIO pin for the LCD data/command signal
- Returns:
The GPIO pin for the LCD data/command signal
-
using button_callback_t = espp::Interrupt::event_callback_fn