LCD Screens

⚠️

This documentation is under construction and incomplete. We are in the process of choosing a microphone that works well with our robots.

LCD Control Interfaces

I2C Interface

The Watch LCD communicates with the RP2040 MCU via I2C. The I2C interface uses:

  • SDA (Data line) and SCL (Clock line)
  • Typical communication speed of 400kHz
  • Commands are sent as byte sequences to control display parameters

HDMI Interface

The Large LCD connects via HDMI to the Jetson:

  • Supports up to 1024x600 @ 60Hz
  • Uses TMDS (Transition Minimized Differential Signaling)
  • Audio is carried over the same cable to the built-in speakers

Display Hardware

Large LCD for Face

We are using this LCD for the face.

Watch LCD for Eyes

At the moment, we are using this LCD from waveshare: https://www.waveshare.com/product/rp2040-lcd-1.28.htm It is an 1.28in circular display connected to a waveshare RP2040 microcontroller. It also has an IMU built in.

We selected it for its compact form factor and having the onboard microcontroller helps simplify the firmware code on the Jetson.

It can be programmed using the Pico SDK in C or with CircuitPython or MicroPython.

Implementation with C

For the eye animation, we use the LVGL graphics library. Waveshare provides helper libraries:

Example eye animation code:

void Grinning_Emotion_Init_Anim(void)
{
    if (anim_enabled) return;
 
    lv_obj_clean(lv_scr_act()); // Get current screen and clean it
    lv_obj_set_style_bg_color(lv_scr_act(), lv_color_white(), 0 );
 
    lv_obj_t *arcPointer = lv_arc_create(lv_scr_act());
 
 
    lv_obj_set_size(arcPointer, 175, 175);
    lv_arc_set_bg_angles(arcPointer, 190, 350);
    lv_arc_set_angles(arcPointer, 0, 0);
    lv_obj_remove_style(arcPointer, NULL, LV_PART_KNOB);
    lv_obj_set_style_arc_color(arcPointer, lv_palette_main(LV_PALETTE_BROWN), 0);
    lv_obj_center(arcPointer);
    lv_obj_set_style_arc_width(arcPointer, 19, LV_PART_MAIN);
 
 
    // Define animation
    lv_anim_t a;
    lv_anim_init(&a);
 
    //Params of animation
    lv_anim_set_var(&a, arcPointer);
    lv_anim_set_time(&a, 3000);
    // lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
    lv_anim_set_path_cb(&a, lv_anim_path_ease_in_out);
    // lv_anim_set_playback_time(&a, 1000);
 
 
    // Direct to callback to apply value
    lv_anim_set_exec_cb(&a, arc_start_angle_cb);
    lv_anim_set_values(&a, 205, 190);
    lv_anim_start(&a);
    lv_anim_set_exec_cb(&a, arc_end_angle_cb);
    lv_anim_set_values(&a, 335, 350);
    lv_anim_start(&a);
 
    anim_enabled = true;
    logo_enabled = false;
}

For reading the data from the IMU, use the QMI8658 library on the Waveshare demo repo /lib/QMI8658. See the full sample code at their github repo, a short snippet is included below

#include "QMI8658.h"
 
QMI8658_init(); //located in LCD_1in28_LVGL_test.c
 
//...
add_repeating_timer_ms(50, repeating_imu_data_update_timer_callback, NULL, &imu_data_update_timer);
add_repeating_timer_ms(100, repeating_imu_diff_timer_callback, NULL, &imu_diff_timer);
add_repeating_timer_ms(1,   repeating_lvgl_timer_callback, NULL, &lvgl_timer);
 
char label_text[64];
float acc[3], gyro[3];
unsigned int tim_count = 0;
 
QMI8658_read_xyz(acc, gyro, &tim_count);

CircuitPython

An alternative choice is to flash the RP2040 with CircuitPython and then write Python code to load onto it. An example open source implementation is located here. You can display an animation with the below functionalities:

hardware = wsRP2040128()
hardware.draw_circle('eye', 115, 120, 100, hardware.color('white'))
hardware.sprites['eye'].x = 80
 
sleep_time = 0.01
moving_pos = True
 
while True:
    if(hardware.sprites['eye'].x < 80):
        moving_pos = True
        hardware.sprites['eye'].x = 80
    if(hardware.sprites['eye'].x >170):
        moving_pos = False
        hardware.sprites['eye'].x = 170
 
    if moving_pos:
        hardware.sprites['eye'].x += 2
    else:
        hardware.sprites['eye'].x -= 2
 
    if(hardware.time + sleep_time) < time.monotonic():
        hardware.update()
        hardware.time = time.monotonic()
 
    time.sleep(sleep_time)