#include "m5u_shim.h"
#include <M5Unified.h>
#include <driver/gpio.h>
#include <driver/sdspi_host.h>
#include <driver/spi_common.h>
#include <esp_err.h>
#include <esp_vfs_fat.h>
#include <sdmmc_cmd.h>
#include <string>
static std::string s_m5u_log_suffixes[3];
static m5u_log_callback_t s_m5u_log_callback = nullptr;
static void* s_m5u_log_callback_user_data = nullptr;
static constexpr const char* M5U_SD_MOUNT_POINT = "/sdcard";
static sdmmc_card_t* s_m5u_sd_card = nullptr;
static spi_host_device_t s_m5u_sd_host = SPI2_HOST;
static bool s_m5u_sd_owns_bus = false;
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32P4)
#define M5U_HAS_AXP2101 0
#else
#define M5U_HAS_AXP2101 1
#endif
extern "C" {
static auto m5u_apply_config(const m5u_config_t* src) {
auto cfg = M5.config();
if (!src) {
return cfg;
}
#if defined(ARDUINO)
cfg.serial_baudrate = src->serial_baudrate;
#endif
cfg.external_speaker_value = src->external_speaker_value;
cfg.external_display_value = src->external_display_value;
cfg.clear_display = src->clear_display != 0;
cfg.output_power = src->output_power != 0;
cfg.pmic_button = src->pmic_button != 0;
cfg.internal_imu = src->internal_imu != 0;
cfg.internal_rtc = src->internal_rtc != 0;
cfg.internal_mic = src->internal_mic != 0;
cfg.internal_spk = src->internal_spk != 0;
cfg.external_imu = src->external_imu != 0;
cfg.external_rtc = src->external_rtc != 0;
cfg.disable_rtc_irq = src->disable_rtc_irq != 0;
cfg.led_brightness = src->led_brightness;
if (src->fallback_board >= 0) {
cfg.fallback_board = (m5::board_t)src->fallback_board;
}
return cfg;
}
bool m5u_begin(void) {
auto cfg = m5u_apply_config(nullptr);
M5.begin(cfg);
return true;
}
bool m5u_begin_with_config(const m5u_config_t* config) {
auto cfg = m5u_apply_config(config);
M5.begin(cfg);
return true;
}
void m5u_update(void) {
M5.update();
}
void m5u_delay_ms(uint32_t ms) {
M5.delay(ms);
}
int m5u_get_board(void) {
return (int)M5.getBoard();
}
int m5u_get_pin(int name) {
return M5.getPin((m5::pin_name_t)name);
}
bool m5u_set_primary_display_index(size_t index) {
return M5.setPrimaryDisplay(index);
}
bool m5u_set_primary_display_type(int kind) {
return M5.setPrimaryDisplayType((m5gfx::board_t)kind);
}
void m5u_set_log_display_index(size_t index) {
M5.setLogDisplayIndex(index);
}
void m5u_set_log_display_type(int kind) {
M5.setLogDisplayType((m5gfx::board_t)kind);
}
void m5u_set_touch_button_height(uint16_t pixel) {
M5.setTouchButtonHeight(pixel);
}
void m5u_set_touch_button_height_by_ratio(uint8_t ratio) {
M5.setTouchButtonHeightByRatio(ratio);
}
uint16_t m5u_get_touch_button_height(void) {
return M5.getTouchButtonHeight();
}
int m5u_display_width(void) {
return M5.Display.width();
}
int m5u_display_height(void) {
return M5.Display.height();
}
void m5u_display_fill_screen(uint16_t color) {
M5.Display.fillScreen(color);
}
void m5u_display_set_cursor(int x, int y) {
M5.Display.setCursor(x, y);
}
void m5u_display_set_text_size(int size) {
M5.Display.setTextSize(size);
}
void m5u_display_set_text_color(uint16_t fg, uint16_t bg) {
M5.Display.setTextColor(fg, bg);
}
void m5u_display_print(const char* text) {
M5.Display.print(text);
}
void m5u_display_println(const char* text) {
M5.Display.println(text);
}
void m5u_display_draw_line(int x0, int y0, int x1, int y1, uint16_t color) {
M5.Display.drawLine(x0, y0, x1, y1, color);
}
void m5u_display_draw_rect(int x, int y, int w, int h, uint16_t color) {
M5.Display.drawRect(x, y, w, h, color);
}
void m5u_display_fill_rect(int x, int y, int w, int h, uint16_t color) {
M5.Display.fillRect(x, y, w, h, color);
}
void m5u_display_draw_circle(int x, int y, int r, uint16_t color) {
M5.Display.drawCircle(x, y, r, color);
}
void m5u_display_fill_circle(int x, int y, int r, uint16_t color) {
M5.Display.fillCircle(x, y, r, color);
}
void m5u_display_set_rotation(int rotation) {
M5.Display.setRotation(rotation);
}
bool m5u_btn_a_is_pressed(void) {
return M5.BtnA.isPressed();
}
bool m5u_btn_a_was_pressed(void) {
return M5.BtnA.wasPressed();
}
bool m5u_btn_a_was_released(void) {
return M5.BtnA.wasReleased();
}
bool m5u_btn_b_is_pressed(void) {
return M5.BtnB.isPressed();
}
bool m5u_btn_b_was_pressed(void) {
return M5.BtnB.wasPressed();
}
bool m5u_btn_b_was_released(void) {
return M5.BtnB.wasReleased();
}
bool m5u_btn_c_is_pressed(void) {
return M5.BtnC.isPressed();
}
bool m5u_btn_c_was_pressed(void) {
return M5.BtnC.wasPressed();
}
bool m5u_btn_c_was_released(void) {
return M5.BtnC.wasReleased();
}
bool m5u_mic_begin(void) {
return M5.Mic.begin();
}
bool m5u_mic_record_i16(int16_t* buffer, size_t samples) {
return M5.Mic.record(buffer, samples);
}
bool m5u_speaker_begin(void) {
return M5.Speaker.begin();
}
void m5u_speaker_set_volume(uint8_t volume) {
M5.Speaker.setVolume(volume);
}
bool m5u_speaker_tone(uint32_t frequency_hz, uint32_t duration_ms) {
return M5.Speaker.tone(frequency_hz, duration_ms);
}
bool m5u_speaker_play_i16(const int16_t* samples, size_t len, uint32_t sample_rate_hz) {
return M5.Speaker.playRaw(samples, len, sample_rate_hz, false, 1, 0);
}
bool m5u_imu_begin(void) {
return M5.Imu.begin();
}
bool m5u_imu_get_accel(float* x, float* y, float* z) {
return M5.Imu.getAccel(x, y, z);
}
bool m5u_imu_get_gyro(float* x, float* y, float* z) {
return M5.Imu.getGyro(x, y, z);
}
bool m5u_imu_get_mag(float* x, float* y, float* z) {
return M5.Imu.getMag(x, y, z);
}
bool m5u_imu_get_data(m5u_imu_data_t* out) {
if (!M5.Imu.isEnabled() || !out) {
return false;
}
auto data = M5.Imu.getImuData();
out->usec = data.usec;
out->accel_x = data.accel.x;
out->accel_y = data.accel.y;
out->accel_z = data.accel.z;
out->gyro_x = data.gyro.x;
out->gyro_y = data.gyro.y;
out->gyro_z = data.gyro.z;
out->mag_x = data.mag.x;
out->mag_y = data.mag.y;
out->mag_z = data.mag.z;
return true;
}
bool m5u_imu_get_temp_c(float* temp) {
return M5.Imu.getTemp(temp);
}
int m5u_touch_count(void) {
return M5.Touch.getCount();
}
bool m5u_touch_get(int index, int* x, int* y) {
auto detail = M5.Touch.getDetail(index);
if (x) { *x = detail.x; }
if (y) { *y = detail.y; }
return detail.isPressed();
}
bool m5u_rtc_get_datetime(int* year, int* month, int* day, int* hour, int* minute, int* second) {
m5::rtc_datetime_t dt;
if (!M5.Rtc.getDateTime(&dt)) { return false; }
if (year) { *year = dt.date.year; }
if (month) { *month = dt.date.month; }
if (day) { *day = dt.date.date; }
if (hour) { *hour = dt.time.hours; }
if (minute) { *minute = dt.time.minutes; }
if (second) { *second = dt.time.seconds; }
return true;
}
static void m5u_rtc_to_raw(const m5::rtc_datetime_t& src, m5u_rtc_datetime_t* out) {
out->year = src.date.year;
out->month = src.date.month;
out->day = src.date.date;
out->weekday = src.date.weekDay;
out->hour = src.time.hours;
out->minute = src.time.minutes;
out->second = src.time.seconds;
}
static m5::rtc_datetime_t m5u_rtc_from_raw(const m5u_rtc_datetime_t* src) {
m5::rtc_datetime_t dt = {};
dt.date.year = src->year;
dt.date.month = src->month;
dt.date.date = src->day;
dt.date.weekDay = src->weekday;
dt.time.hours = src->hour;
dt.time.minutes = src->minute;
dt.time.seconds = src->second;
return dt;
}
bool m5u_rtc_get_datetime_detail(m5u_rtc_datetime_t* out) {
if (!out) {
return false;
}
m5::rtc_datetime_t dt;
if (!M5.Rtc.getDateTime(&dt)) {
return false;
}
m5u_rtc_to_raw(dt, out);
return true;
}
bool m5u_rtc_set_datetime(int year, int month, int day, int hour, int minute, int second) {
m5::rtc_datetime_t dt;
dt.date.year = year;
dt.date.month = month;
dt.date.date = day;
dt.time.hours = hour;
dt.time.minutes = minute;
dt.time.seconds = second;
M5.Rtc.setDateTime(&dt);
return true;
}
bool m5u_rtc_set_datetime_detail(const m5u_rtc_datetime_t* datetime) {
if (!datetime) {
return false;
}
auto dt = m5u_rtc_from_raw(datetime);
M5.Rtc.setDateTime(&dt);
return true;
}
void m5u_rtc_set_system_time_from_rtc(void) {
M5.Rtc.setSystemTimeFromRtc();
}
bool m5u_rtc_get_volt_low(void) {
return M5.Rtc.getVoltLow();
}
uint32_t m5u_rtc_set_timer_irq(uint32_t timer_msec) {
return M5.Rtc.setTimerIRQ(timer_msec);
}
int m5u_rtc_set_alarm_irq_after_seconds(int after_seconds) {
return M5.Rtc.setAlarmIRQ(after_seconds);
}
int m5u_rtc_set_alarm_irq_datetime(const m5u_rtc_datetime_t* datetime) {
if (!datetime) {
return -1;
}
auto dt = m5u_rtc_from_raw(datetime);
return M5.Rtc.setAlarmIRQ(&dt.date, &dt.time);
}
bool m5u_rtc_get_irq_status(void) {
return M5.Rtc.getIRQstatus();
}
void m5u_rtc_clear_irq(void) {
M5.Rtc.clearIRQ();
}
void m5u_rtc_disable_irq(void) {
M5.Rtc.disableIRQ();
}
int m5u_battery_level(void) {
return M5.Power.getBatteryLevel();
}
int m5u_battery_voltage_mv(void) {
return M5.Power.getBatteryVoltage();
}
int m5u_power_get_type(void) {
return (int)M5.Power.getType();
}
int m5u_power_get_charge_state(void) {
return (int)M5.Power.isCharging();
}
bool m5u_power_is_charging(void) {
return M5.Power.isCharging() == m5::Power_Class::is_charging_t::is_charging;
}
void m5u_power_set_led(uint8_t brightness) {
M5.Power.setLed(brightness);
}
void m5u_power_set_ext_output(bool enable, uint16_t port_mask) {
M5.Power.setExtOutput(enable, (m5::ext_port_mask_t)port_mask);
}
bool m5u_power_get_ext_output(void) {
return M5.Power.getExtOutput();
}
void m5u_power_set_usb_output(bool enable) {
M5.Power.setUsbOutput(enable);
}
bool m5u_power_get_usb_output(void) {
return M5.Power.getUsbOutput();
}
void m5u_power_set_battery_charge(bool enable) {
M5.Power.setBatteryCharge(enable);
}
void m5u_power_set_charge_current(uint16_t max_ma) {
M5.Power.setChargeCurrent(max_ma);
}
void m5u_power_set_charge_voltage(uint16_t max_mv) {
M5.Power.setChargeVoltage(max_mv);
}
int m5u_power_get_vbus_voltage_mv(void) {
return M5.Power.getVBUSVoltage();
}
int m5u_power_get_battery_current_ma(void) {
return M5.Power.getBatteryCurrent();
}
uint8_t m5u_power_get_key_state(void) {
return M5.Power.getKeyState();
}
void m5u_power_set_vibration(uint8_t level) {
M5.Power.setVibration(level);
}
void m5u_log_println(const char* text) {
M5_LOGI("%s", text);
}
bool m5u_sd_begin(void) {
m5u_sd_spi_config_t config = {};
config.pin_sclk = M5.getPin(m5::pin_name_t::sd_spi_sclk);
config.pin_mosi = M5.getPin(m5::pin_name_t::sd_spi_mosi);
config.pin_miso = M5.getPin(m5::pin_name_t::sd_spi_miso);
config.pin_cs = M5.getPin(m5::pin_name_t::sd_spi_cs);
config.host_id = -1;
config.frequency_khz = 20000;
config.max_files = 5;
config.format_if_mount_failed = 0;
return m5u_sd_begin_spi(&config);
}
bool m5u_sd_begin_spi(const m5u_sd_spi_config_t* config) {
if (s_m5u_sd_card) {
return true;
}
if (!config || config->pin_sclk < 0 || config->pin_mosi < 0 || config->pin_miso < 0 || config->pin_cs < 0) {
return false;
}
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
spi_host_device_t spi_host = static_cast<spi_host_device_t>(host.slot);
if (config->host_id >= 0) {
spi_host = static_cast<spi_host_device_t>(config->host_id);
host.slot = static_cast<int>(spi_host);
}
if (config->frequency_khz > 0) {
host.max_freq_khz = config->frequency_khz;
}
spi_bus_config_t bus_config = {};
bus_config.mosi_io_num = config->pin_mosi;
bus_config.miso_io_num = config->pin_miso;
bus_config.sclk_io_num = config->pin_sclk;
bus_config.quadwp_io_num = GPIO_NUM_NC;
bus_config.quadhd_io_num = GPIO_NUM_NC;
bus_config.max_transfer_sz = 4000;
esp_err_t err = spi_bus_initialize(spi_host, &bus_config, SDSPI_DEFAULT_DMA);
bool owns_bus = false;
if (err == ESP_OK) {
owns_bus = true;
} else if (err != ESP_ERR_INVALID_STATE) {
return false;
}
sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
slot_config.gpio_cs = (gpio_num_t)config->pin_cs;
slot_config.host_id = spi_host;
esp_vfs_fat_mount_config_t mount_config = VFS_FAT_MOUNT_DEFAULT_CONFIG();
mount_config.format_if_mount_failed = config->format_if_mount_failed != 0;
if (config->max_files > 0) {
mount_config.max_files = config->max_files;
}
err = esp_vfs_fat_sdspi_mount(M5U_SD_MOUNT_POINT, &host, &slot_config, &mount_config, &s_m5u_sd_card);
if (err != ESP_OK) {
s_m5u_sd_card = nullptr;
if (owns_bus) {
spi_bus_free(spi_host);
}
return false;
}
s_m5u_sd_host = spi_host;
s_m5u_sd_owns_bus = owns_bus;
return true;
}
bool m5u_sd_is_mounted(void) {
return s_m5u_sd_card != nullptr;
}
void m5u_sd_end(void) {
if (!s_m5u_sd_card) {
return;
}
esp_vfs_fat_sdcard_unmount(M5U_SD_MOUNT_POINT, s_m5u_sd_card);
s_m5u_sd_card = nullptr;
if (s_m5u_sd_owns_bus) {
spi_bus_free(s_m5u_sd_host);
}
s_m5u_sd_owns_bus = false;
}
static bool m5u_button_state(int button, int query) {
m5::Button_Class* btn = nullptr;
switch (button) {
case 0: btn = &M5.BtnA; break;
case 1: btn = &M5.BtnB; break;
case 2: btn = &M5.BtnC; break;
case 3: btn = &M5.BtnPWR; break;
case 4: btn = &M5.BtnEXT; break;
default: return false;
}
switch (query) {
case 0: return btn->isPressed();
case 1: return btn->wasPressed();
case 2: return btn->wasReleased();
case 3: return btn->wasClicked();
case 4: return btn->wasHold();
case 5: return btn->isHolding();
case 6: return btn->wasDecideClickCount();
default: return false;
}
}
int m5u_display_get_rotation(void) {
return M5.Display.getRotation();
}
void m5u_display_set_brightness(uint8_t brightness) {
M5.Display.setBrightness(brightness);
}
void m5u_display_set_epd_fastest(void) {
M5.Display.setEpdMode(m5gfx::epd_fastest);
}
void m5u_display_set_epd_mode(int mode) {
switch (mode) {
case m5gfx::epd_quality:
case m5gfx::epd_text:
case m5gfx::epd_fast:
case m5gfx::epd_fastest:
M5.Display.setEpdMode((m5gfx::epd_mode_t)mode);
break;
default:
break;
}
}
void m5u_display_set_text_scroll(bool scroll) {
M5.Display.setTextScroll(scroll);
}
bool m5u_display_set_font(int font) {
switch (font) {
case 0:
M5.Display.setFont(nullptr);
return true;
case 1:
M5.Display.setFont(&fonts::AsciiFont8x16);
return true;
case 2:
M5.Display.setFont(&fonts::lgfxJapanGothic_12);
return true;
case 3:
M5.Display.setFont(&fonts::DejaVu18);
return true;
default:
return false;
}
}
void m5u_display_start_write(void) {
M5.Display.startWrite();
}
void m5u_display_end_write(void) {
M5.Display.endWrite();
}
void m5u_display_display(void) {
M5.Display.display();
}
bool m5u_display_display_busy(void) {
return M5.Display.displayBusy();
}
void m5u_display_wait_display(void) {
M5.Display.waitDisplay();
}
int m5u_display_get_cursor_y(void) {
return M5.Display.getCursorY();
}
int m5u_display_font_height(void) {
return M5.Display.fontHeight();
}
uint16_t m5u_display_get_base_color(void) {
return M5.Display.getBaseColor();
}
void m5u_display_set_color(uint16_t color) {
M5.Display.setColor(color);
}
void m5u_display_set_text_wrap(bool wrap_x, bool wrap_y) {
M5.Display.setTextWrap(wrap_x, wrap_y);
}
void m5u_display_set_text_datum(int datum) {
M5.Display.setTextDatum((textdatum_t)datum);
}
int m5u_display_draw_string(const char* text, int x, int y) {
return M5.Display.drawString(text, x, y);
}
void m5u_display_write_pixel(int x, int y, uint16_t color) {
M5.Display.writePixel(x, y, color);
}
void m5u_display_write_fast_vline(int x, int y, int h, uint16_t color) {
M5.Display.writeFastVLine(x, y, h, color);
}
void m5u_display_set_clip_rect(int x, int y, int w, int h) {
M5.Display.setClipRect(x, y, w, h);
}
void m5u_display_clear_clip_rect(void) {
M5.Display.clearClipRect();
}
uint16_t m5u_display_color888(uint8_t r, uint8_t g, uint8_t b) {
return M5.Display.color888(r, g, b);
}
int m5u_display_count(void) {
return M5.getDisplayCount();
}
int m5u_display_index_for_kind(int kind) {
return M5.getDisplayIndex((m5::board_t)kind);
}
int m5u_display_width_at(int index) {
return M5.Displays(index).width();
}
int m5u_display_height_at(int index) {
return M5.Displays(index).height();
}
void m5u_display_set_text_size_at(int index, int size) {
M5.Displays(index).setTextSize(size);
}
void m5u_display_start_write_at(int index) {
M5.Displays(index).startWrite();
}
void m5u_display_end_write_at(int index) {
M5.Displays(index).endWrite();
}
void m5u_display_print_at(int index, const char* text) {
M5.Displays(index).print(text);
}
void m5u_display_println_at(int index, const char* text) {
M5.Displays(index).println(text);
}
int m5u_display_draw_string_at(int index, const char* text, int x, int y) {
return M5.Displays(index).drawString(text, x, y);
}
void m5u_display_fill_rect_at(int index, int x, int y, int w, int h, uint16_t color) {
M5.Displays(index).fillRect(x, y, w, h, color);
}
void m5u_display_fill_circle_at(int index, int x, int y, int r, uint16_t color) {
M5.Displays(index).fillCircle(x, y, r, color);
}
void m5u_display_write_pixel_at(int index, int x, int y, uint16_t color) {
M5.Displays(index).writePixel(x, y, color);
}
void m5u_display_draw_pixel_at(int index, int x, int y, uint16_t color) {
M5.Displays(index).drawPixel(x, y, color);
}
bool m5u_button_is_pressed(int button) { return m5u_button_state(button, 0); }
bool m5u_button_was_pressed(int button) { return m5u_button_state(button, 1); }
bool m5u_button_was_released(int button) { return m5u_button_state(button, 2); }
bool m5u_button_was_clicked(int button) { return m5u_button_state(button, 3); }
bool m5u_button_was_hold(int button) { return m5u_button_state(button, 4); }
bool m5u_button_is_holding(int button) { return m5u_button_state(button, 5); }
bool m5u_button_was_decide_click_count(int button) { return m5u_button_state(button, 6); }
int m5u_button_get_click_count(int button) {
switch (button) {
case 0: return M5.BtnA.getClickCount();
case 1: return M5.BtnB.getClickCount();
case 2: return M5.BtnC.getClickCount();
case 3: return M5.BtnPWR.getClickCount();
case 4: return M5.BtnEXT.getClickCount();
default: return 0;
}
}
bool m5u_mic_is_enabled(void) {
return M5.Mic.isEnabled();
}
bool m5u_mic_is_recording(void) {
return M5.Mic.isRecording();
}
void m5u_mic_end(void) {
M5.Mic.end();
}
bool m5u_mic_record_i16_at(int16_t* buffer, size_t samples, uint32_t sample_rate_hz) {
return M5.Mic.record(buffer, samples, sample_rate_hz);
}
bool m5u_mic_get_config(m5u_mic_config_t* out) {
if (!out) {
return false;
}
auto cfg = M5.Mic.config();
out->pin_data_in = cfg.pin_data_in;
out->pin_bck = cfg.pin_bck;
out->pin_mck = cfg.pin_mck;
out->pin_ws = cfg.pin_ws;
out->sample_rate = cfg.sample_rate;
out->left_channel = cfg.left_channel;
out->stereo = cfg.stereo;
out->over_sampling = cfg.over_sampling;
out->magnification = cfg.magnification;
out->noise_filter_level = cfg.noise_filter_level;
out->use_adc = cfg.use_adc;
out->dma_buf_len = cfg.dma_buf_len;
out->dma_buf_count = cfg.dma_buf_count;
out->task_priority = cfg.task_priority;
out->task_pinned_core = cfg.task_pinned_core;
out->i2s_port = (int)cfg.i2s_port;
return true;
}
bool m5u_mic_set_config(const m5u_mic_config_t* config) {
if (!config) {
return false;
}
auto cfg = M5.Mic.config();
cfg.pin_data_in = config->pin_data_in;
cfg.pin_bck = config->pin_bck;
cfg.pin_mck = config->pin_mck;
cfg.pin_ws = config->pin_ws;
cfg.sample_rate = config->sample_rate;
cfg.left_channel = config->left_channel != 0;
cfg.stereo = config->stereo != 0;
cfg.over_sampling = config->over_sampling;
cfg.magnification = config->magnification;
cfg.noise_filter_level = config->noise_filter_level;
cfg.use_adc = config->use_adc != 0;
cfg.dma_buf_len = config->dma_buf_len;
cfg.dma_buf_count = config->dma_buf_count;
cfg.task_priority = config->task_priority;
cfg.task_pinned_core = config->task_pinned_core;
cfg.i2s_port = (i2s_port_t)config->i2s_port;
M5.Mic.config(cfg);
return true;
}
int m5u_mic_get_noise_filter_level(void) {
return M5.Mic.config().noise_filter_level;
}
bool m5u_mic_set_noise_filter_level(int level) {
auto cfg = M5.Mic.config();
cfg.noise_filter_level = level;
M5.Mic.config(cfg);
return true;
}
bool m5u_speaker_is_enabled(void) {
return M5.Speaker.isEnabled();
}
void m5u_speaker_end(void) {
M5.Speaker.end();
}
uint8_t m5u_speaker_get_volume(void) {
return M5.Speaker.getVolume();
}
bool m5u_speaker_get_config(m5u_speaker_config_t* out) {
if (!out) {
return false;
}
auto cfg = M5.Speaker.config();
out->pin_data_out = cfg.pin_data_out;
out->pin_bck = cfg.pin_bck;
out->pin_mck = cfg.pin_mck;
out->pin_ws = cfg.pin_ws;
out->sample_rate = cfg.sample_rate;
out->stereo = cfg.stereo;
out->buzzer = cfg.buzzer;
out->use_dac = cfg.use_dac;
out->dac_zero_level = cfg.dac_zero_level;
out->magnification = cfg.magnification;
out->dma_buf_len = cfg.dma_buf_len;
out->dma_buf_count = cfg.dma_buf_count;
out->task_priority = cfg.task_priority;
out->task_pinned_core = cfg.task_pinned_core;
out->i2s_port = (int)cfg.i2s_port;
return true;
}
bool m5u_speaker_set_config(const m5u_speaker_config_t* config) {
if (!config) {
return false;
}
auto cfg = M5.Speaker.config();
cfg.pin_data_out = config->pin_data_out;
cfg.pin_bck = config->pin_bck;
cfg.pin_mck = config->pin_mck;
cfg.pin_ws = config->pin_ws;
cfg.sample_rate = config->sample_rate;
cfg.stereo = config->stereo != 0;
cfg.buzzer = config->buzzer != 0;
cfg.use_dac = config->use_dac != 0;
cfg.dac_zero_level = config->dac_zero_level;
cfg.magnification = config->magnification;
cfg.dma_buf_len = config->dma_buf_len;
cfg.dma_buf_count = config->dma_buf_count;
cfg.task_priority = config->task_priority;
cfg.task_pinned_core = config->task_pinned_core;
cfg.i2s_port = (i2s_port_t)config->i2s_port;
M5.Speaker.config(cfg);
return true;
}
bool m5u_speaker_tone_ex(float frequency_hz, uint32_t duration_ms, int channel) {
return M5.Speaker.tone(frequency_hz, duration_ms, channel);
}
bool m5u_speaker_play_u8(const uint8_t* samples, size_t len, uint32_t sample_rate_hz) {
return M5.Speaker.playRaw(samples, len, sample_rate_hz, false, 1, 0);
}
bool m5u_speaker_play_wav(const uint8_t* data, size_t len) {
return M5.Speaker.playWav(data, len);
}
bool m5u_speaker_is_playing(int channel) {
return channel < 0 ? M5.Speaker.isPlaying() : M5.Speaker.isPlaying(channel);
}
void m5u_speaker_stop(int channel) {
if (channel < 0) { M5.Speaker.stop(); } else { M5.Speaker.stop(channel); }
}
uint8_t m5u_speaker_get_channel_volume(int channel) {
return M5.Speaker.getChannelVolume(channel);
}
void m5u_speaker_set_channel_volume(int channel, uint8_t volume) {
M5.Speaker.setChannelVolume(channel, volume);
}
void m5u_speaker_set_all_channel_volume(uint8_t volume) {
M5.Speaker.setAllChannelVolume(volume);
}
bool m5u_imu_is_enabled(void) {
return M5.Imu.isEnabled();
}
int m5u_imu_get_type(void) {
return (int)M5.Imu.getType();
}
bool m5u_imu_update(void) {
return M5.Imu.update();
}
bool m5u_imu_load_offset_from_nvs(void) {
return M5.Imu.loadOffsetFromNVS();
}
bool m5u_imu_save_offset_to_nvs(void) {
return M5.Imu.saveOffsetToNVS();
}
float m5u_imu_get_offset_data(int index) {
return M5.Imu.getOffsetData(index);
}
void m5u_imu_set_calibration(float x, float y, float z) {
M5.Imu.setCalibration(x, y, z);
}
bool m5u_touch_get_detail(int index, m5u_touch_detail_t* out) {
if (!out) { return false; }
auto d = M5.Touch.getDetail(index);
out->x = d.x;
out->y = d.y;
out->prev_x = d.prev_x;
out->prev_y = d.prev_y;
out->is_pressed = d.isPressed();
out->was_pressed = d.wasPressed();
out->was_released = d.wasReleased();
out->was_clicked = d.wasClicked();
out->was_hold = d.wasHold();
out->is_holding = d.isHolding();
out->click_count = d.getClickCount();
return true;
}
bool m5u_rtc_is_enabled(void) {
return M5.Rtc.isEnabled();
}
static bool m5u_has_axp2101(void) {
#if M5U_HAS_AXP2101
return M5.Power.getType() == m5::Power_Class::pmic_t::pmic_axp2101;
#else
return false;
#endif
}
bool m5u_power_axp2101_disable_irq(uint64_t mask) {
#if M5U_HAS_AXP2101
return m5u_has_axp2101() && M5.Power.Axp2101.disableIRQ(mask);
#else
(void)mask;
return false;
#endif
}
bool m5u_power_axp2101_enable_irq(uint64_t mask) {
#if M5U_HAS_AXP2101
return m5u_has_axp2101() && M5.Power.Axp2101.enableIRQ(mask);
#else
(void)mask;
return false;
#endif
}
bool m5u_power_axp2101_clear_irq_statuses(void) {
#if M5U_HAS_AXP2101
if (!m5u_has_axp2101()) {
return false;
}
M5.Power.Axp2101.clearIRQStatuses();
return true;
#else
return false;
#endif
}
uint64_t m5u_power_axp2101_get_irq_statuses(void) {
#if M5U_HAS_AXP2101
return m5u_has_axp2101() ? M5.Power.Axp2101.getIRQStatuses() : 0;
#else
return 0;
#endif
}
bool m5u_power_axp2101_is_bat_charger_under_temperature_irq(void) {
#if M5U_HAS_AXP2101
return m5u_has_axp2101() && M5.Power.Axp2101.isBatChargerUnderTemperatureIrq();
#else
return false;
#endif
}
bool m5u_power_axp2101_is_bat_charger_over_temperature_irq(void) {
#if M5U_HAS_AXP2101
return m5u_has_axp2101() && M5.Power.Axp2101.isBatChargerOverTemperatureIrq();
#else
return false;
#endif
}
bool m5u_power_axp2101_is_vbus_insert_irq(void) {
#if M5U_HAS_AXP2101
return m5u_has_axp2101() && M5.Power.Axp2101.isVbusInsertIrq();
#else
return false;
#endif
}
bool m5u_power_axp2101_is_vbus_remove_irq(void) {
#if M5U_HAS_AXP2101
return m5u_has_axp2101() && M5.Power.Axp2101.isVbusRemoveIrq();
#else
return false;
#endif
}
bool m5u_led_begin(void) {
return M5.Led.begin();
}
void m5u_led_display(void) {
M5.Led.display();
}
void m5u_led_set_auto_display(bool enable) {
M5.Led.setAutoDisplay(enable);
}
size_t m5u_led_count(void) {
return M5.Led.getCount();
}
void m5u_led_set_brightness(uint8_t brightness) {
M5.Led.setBrightness(brightness);
}
void m5u_led_set_color_rgb(size_t index, uint8_t r, uint8_t g, uint8_t b) {
M5.Led.setColor(index, r, g, b);
}
void m5u_led_set_all_color_rgb(uint8_t r, uint8_t g, uint8_t b) {
M5.Led.setAllColor(RGBColor{r, g, b});
}
bool m5u_led_is_enabled(void) {
return M5.Led.isEnabled();
}
void m5u_log_print(const char* text) {
M5.Log.print(text);
}
void m5u_log_level(int level, const char* text) {
M5.Log((esp_log_level_t)level, "%s", text);
}
bool m5u_log_set_callback(m5u_log_callback_t callback, void* user_data) {
s_m5u_log_callback = callback;
s_m5u_log_callback_user_data = user_data;
if (!callback) {
M5.Log.setCallback(nullptr);
return true;
}
M5.Log.setCallback([](esp_log_level_t level, bool use_color, const char* text) {
if (s_m5u_log_callback) {
s_m5u_log_callback((int)level, use_color, text, s_m5u_log_callback_user_data);
}
});
return true;
}
static bool m5u_log_valid_target(int target) {
return target >= m5::log_target_serial && target < m5::log_target_max;
}
static bool m5u_log_valid_level(int level) {
return level >= ESP_LOG_NONE && level <= ESP_LOG_VERBOSE;
}
bool m5u_log_set_enable_color(int target, bool enable) {
if (!m5u_log_valid_target(target)) {
return false;
}
M5.Log.setEnableColor((m5::log_target_t)target, enable);
return true;
}
bool m5u_log_get_enable_color(int target) {
if (!m5u_log_valid_target(target)) {
return false;
}
return M5.Log.getEnableColor((m5::log_target_t)target);
}
bool m5u_log_set_level(int target, int level) {
if (!m5u_log_valid_target(target) || !m5u_log_valid_level(level)) {
return false;
}
M5.Log.setLogLevel((m5::log_target_t)target, (esp_log_level_t)level);
return true;
}
int m5u_log_get_level(int target) {
if (!m5u_log_valid_target(target)) {
return -1;
}
return (int)M5.Log.getLogLevel((m5::log_target_t)target);
}
bool m5u_log_set_suffix(int target, const char* suffix) {
if (!m5u_log_valid_target(target) || !suffix) {
return false;
}
s_m5u_log_suffixes[target] = suffix;
M5.Log.setSuffix((m5::log_target_t)target, s_m5u_log_suffixes[target].c_str());
return true;
}
}