Merge branch 'main' of codeberg.org:cool-tech-zone/tangara-fw

custom
ailurux 1 year ago
commit 4eb1a074f7
  1. 2
      lib/libcppbor/CMakeLists.txt
  2. 1
      lib/libmad/CMakeLists.txt
  3. 1
      lib/luavgl/CMakeLists.txt
  4. 1
      lib/lvgl/env_support/cmake/esp.cmake
  5. 2
      lib/opus/CMakeLists.txt
  6. 2
      lib/speexdsp/CMakeLists.txt
  7. 2
      lib/tremor/CMakeLists.txt
  8. 52
      lua/settings.lua
  9. 2
      src/database/track.cpp
  10. 2
      src/drivers/adc.cpp
  11. 3
      src/drivers/bluetooth.cpp
  12. 44
      src/drivers/haptics.cpp
  13. 50
      src/drivers/include/haptics.hpp
  14. 6
      src/drivers/include/nvs.hpp
  15. 14
      src/drivers/nvs.cpp
  16. 8
      src/lua/lua_version.cpp
  17. 10
      src/system_fsm/booting.cpp
  18. 12
      tools/cmake/common.cmake

@ -5,3 +5,5 @@ idf_component_register(
SRCS cppbor.cpp cppbor_parse.cpp
INCLUDE_DIRS "include/cppbor"
)
target_compile_options(${COMPONENT_LIB} PRIVATE
"-Wno-shadow" "-Wno-deprecated-enum-enum-conversion")

@ -41,6 +41,7 @@ target_compile_options(${COMPONENT_LIB}
$<$<C_COMPILER_ID:AppleClang,Clang,GNU>:-Wno-implicit-function-declaration>
$<$<C_COMPILER_ID:AppleClang,Clang,GNU>:-Wno-stringop-overflow>
$<$<C_COMPILER_ID:AppleClang,Clang,GNU>:-fPIC>
-Wno-implicit-fallthrough
-Ofast
)

@ -2,4 +2,5 @@
#
# SPDX-License-Identifier: GPL-3.0-only
idf_component_register(SRCS "src/luavgl.c" INCLUDE_DIRS "src" REQUIRES "esp-idf-lua" "lvgl")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-cast-function-type" "-Wno-type-limits")

@ -31,6 +31,7 @@ else()
REQUIRES esp_timer)
target_compile_definitions(${COMPONENT_LIB} PUBLIC "-DLV_CONF_INCLUDE_SIMPLE")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-missing-field-initializers" "-Wno-incompatible-pointer-types")
if(CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM)
target_compile_definitions(${COMPONENT_LIB}

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.5)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(OpusPackageVersion)

@ -10,4 +10,4 @@ libspeexdsp/scal.c libspeexdsp/smallft.c
PRIV_INCLUDE_DIRS "libspeexdsp"
)
target_compile_options(${COMPONENT_LIB} PRIVATE -DHAVE_CONFIG_H -Wno-shift-negative-value -Wno-unused-const-variable)
target_compile_options(${COMPONENT_LIB} PRIVATE -DHAVE_CONFIG_H -Wno-shift-negative-value -Wno-unused-const-variable -Wno-incompatible-pointer-types -Wno-unused-function -Wno-unused-variable)

@ -5,4 +5,4 @@ idf_component_register(
SRCS bitwise.c codebook.c dsp.c floor0.c floor1.c floor_lookup.c framing.c
info.c mapping0.c mdct.c misc.c res012.c vorbisfile.c
INCLUDE_DIRS ".")
target_compile_options("${COMPONENT_LIB}" PRIVATE -Ofast -Wno-error=misleading-indentation -Wno-error=maybe-uninitialized -Wno-error=char-subscripts -Wno-error=unused-label)
target_compile_options("${COMPONENT_LIB}" PRIVATE -Ofast -Wno-misleading-indentation -Wno-maybe-uninitialized -Wno-char-subscripts -Wno-unused-label -Wno-shift-negative-value -Wno-unused-variable -Wno-type-limits -Wno-implicit-fallthrough)

@ -403,6 +403,38 @@ local DatabaseSettings = SettingsScreen:new {
end
}
local SamdConfirmation = SettingsScreen:new {
title = "Are you sure?",
createUi = function(self)
SettingsScreen.createUi(self)
self.content:Label {
w = lvgl.PCT(100),
text = "After selecting 'flash', copy the new UF2 file onto the USB drive that appears. The screen will be blank until the update is finished.",
long_mode = lvgl.LABEL.LONG_WRAP,
}
local button_container = self.content:Object {
w = lvgl.PCT(100),
h = lvgl.SIZE_CONTENT,
flex = {
flex_direction = "row",
justify_content = "center",
align_items = "space-evenly",
align_content = "center",
},
pad_top = 4,
pad_column = 4,
}
button_container:add_style(styles.list_item)
local update = button_container:Button {}
update:Label { text = "Flash" }
update:onClicked(function()
require("version").update_samd()
end)
end
}
local FirmwareSettings = SettingsScreen:new {
title = "Firmware",
createUi = function(self)
@ -411,6 +443,26 @@ local FirmwareSettings = SettingsScreen:new {
widgets.Row(self.content, "ESP32", version.esp())
widgets.Row(self.content, "SAMD21", version.samd())
widgets.Row(self.content, "Collator", version.collator())
local button_container = self.content:Object {
w = lvgl.PCT(100),
h = lvgl.SIZE_CONTENT,
flex = {
flex_direction = "row",
justify_content = "center",
align_items = "space-evenly",
align_content = "center",
},
pad_top = 4,
pad_column = 4,
}
button_container:add_style(styles.list_item)
local update = button_container:Button {}
update:Label { text = "Update SAMD21" }
update:onClicked(function()
backstack.push(SamdConfirmation:new())
end)
end
}

@ -234,7 +234,7 @@ auto TrackTags::genres(const std::string_view s) -> void {
std::string src = {s.data(), s.size()};
char* token = std::strtok(src.data(), kGenreDelimiters);
auto trim_and_add = [=](std::string_view s) {
auto trim_and_add = [this](std::string_view s) {
std::string copy = {s.data(), s.size()};
// Trim the left

@ -18,7 +18,7 @@ static const adc_bitwidth_t kAdcBitWidth = ADC_BITWIDTH_12;
static const adc_unit_t kAdcUnit = ADC_UNIT_1;
// Max battery voltage should be a little over 2V due to our divider, so we need
// the max attenuation to properly handle the full range.
static const adc_atten_t kAdcAttenuation = ADC_ATTEN_DB_11;
static const adc_atten_t kAdcAttenuation = ADC_ATTEN_DB_12;
// Corresponds to SENSOR_VP.
static const adc_channel_t kAdcChannel = ADC_CHANNEL_0;

@ -438,7 +438,8 @@ void Disabled::react(const events::Enable&) {
return;
}
if ((err = esp_bluedroid_init() != ESP_OK)) {
esp_bluedroid_config_t cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();
if ((err = esp_bluedroid_init_with_cfg(&cfg) != ESP_OK)) {
ESP_LOGE(kTag, "initialize bluedroid failed %s", esp_err_to_name(err));
return;
}

@ -10,6 +10,7 @@
#include <cstdint>
#include <initializer_list>
#include <mutex>
#include <variant>
#include "assert.h"
#include "driver/gpio.h"
@ -27,15 +28,24 @@ namespace drivers {
static constexpr char kTag[] = "haptics";
static constexpr uint8_t kHapticsAddress = 0x5A;
Haptics::Haptics() {
Haptics::Haptics(const std::variant<ErmMotor, LraMotor>& motor) {
PowerUp();
// TODO: Break out into helper functions
// TODO: Use LRA closed loop instead, loading in calibration data from NVS, or
// running calibration + storing the result first if necessary.
if (std::holds_alternative<ErmMotor>(motor)) {
ESP_LOGI(kTag, "Setting up ERM motor...");
// Put into ERM Open Loop:
// (§8.5.4.1 Programming for ERM Open-Loop Operation)
// - Turn off N_ERM_LRA first
WriteRegister(Register::kControl1,
static_cast<uint8_t>(RegisterDefaults::kControl1) &
WriteRegister(Register::kFeedbackControl,
static_cast<uint8_t>(RegisterDefaults::kFeedbackControl) &
(~ControlMask::kNErmLra));
// - Turn on ERM_OPEN_LOOP
WriteRegister(Register::kControl3,
static_cast<uint8_t>(RegisterDefaults::kControl3) |
@ -44,7 +54,29 @@ Haptics::Haptics() {
// Set library
// TODO(robin): try the other libraries and test response. C is marginal, D
// too much?
WriteRegister(Register::kWaveformLibrary, static_cast<uint8_t>(kDefaultLibrary));
WriteRegister(Register::kWaveformLibrary, static_cast<uint8_t>(kDefaultErmLibrary));
} else if (std::holds_alternative<LraMotor>(motor)) {
ESP_LOGI(kTag, "Setting up LRA motor...");
// TODO:
// auto lraInit = std::get<LraMotor>(motor);
// Put into LRA Open Loop:
// (§8.5.4.1 Programming for LRA Open-Loop Operation)
// - Turn on N_ERM_LRA first
WriteRegister(Register::kFeedbackControl,
static_cast<uint8_t>(RegisterDefaults::kFeedbackControl) &
(ControlMask::kNErmLra));
// - Turn on LRA_OPEN_LOOP
WriteRegister(Register::kControl3,
static_cast<uint8_t>(RegisterDefaults::kControl3) |
ControlMask::kLraOpenLoop);
// Set library; only option is the LRA one for, well, LRA motors.
WriteRegister(Register::kWaveformLibrary, static_cast<uint8_t>(Library::LRA));
}
// Set mode (internal trigger, on writing 1 to Go register)
WriteRegister(Register::kMode, static_cast<uint8_t>(Mode::kInternalTrigger));
@ -93,13 +125,13 @@ auto Haptics::SetWaveformEffect(Effect effect) -> void {
auto Haptics::TourEffects() -> void {
TourEffects(Effect::kFirst, Effect::kLast, kDefaultLibrary);
TourEffects(Effect::kFirst, Effect::kLast, kDefaultErmLibrary);
}
auto Haptics::TourEffects(Library lib) -> void {
TourEffects(Effect::kFirst, Effect::kLast, lib);
}
auto Haptics::TourEffects(Effect from, Effect to) -> void {
TourEffects(from, to, kDefaultLibrary);
TourEffects(from, to, kDefaultErmLibrary);
}
auto Haptics::TourEffects(Effect from, Effect to, Library lib) -> void {
ESP_LOGI(kTag, "With library #%u...", static_cast<uint8_t>(lib));

@ -11,13 +11,24 @@
#include <mutex>
#include <optional>
#include <string>
#include <variant>
namespace drivers {
typedef std::monostate ErmMotor;
struct LraMotor {
// TODO: fill out with calibration data from https://www.ti.com/lit/ds/symlink/drv2605l.pdf
bool hi;
};
class Haptics {
public:
static auto Create() -> Haptics* { return new Haptics(); }
Haptics();
static auto Create(const std::variant<ErmMotor, LraMotor>& motor)
-> Haptics* {
return new Haptics(motor);
}
Haptics(const std::variant<ErmMotor, LraMotor>& motor);
~Haptics();
// Not copyable or movable.
@ -169,11 +180,12 @@ class Haptics {
B = 2, // 3V, Rise: 40-60ms, Brake: 5-15ms
C = 3, // 3V, Rise: 60-80ms, Brake: 10-20ms
D = 4, // 3V, Rise: 100-140ms, Brake: 15-25ms
E = 5 // 3V, Rise: >140ms, Brake: >30ms
// 6 is LRA-only, 7 is 4.5V+
E = 5, // 3V, Rise: >140ms, Brake: >30ms
LRA = 6
// 7 is 4.5V+
};
static constexpr Library kDefaultLibrary = Library::C;
static constexpr Library kDefaultErmLibrary = Library::C;
auto PowerDown() -> void;
auto Reset() -> void;
@ -214,10 +226,12 @@ class Haptics {
};
struct ControlMask {
// Control1
// FeedbackControl
static constexpr uint8_t kNErmLra = 0b10000000;
// Control3
static constexpr uint8_t kErmOpenLoop = 0b00100000;
static constexpr uint8_t kLraOpenLoop = 0b00000001;
};
// §8.6 Register Map
@ -257,12 +271,12 @@ class Haptics {
// A bunch of different options, not grouped
// in any particular sensible way
kControl1 = 0x1A,
kControl2 = 0x1B,
kControl3 = 0x1C,
kControl4 = 0x1D,
kControl5 = 0x1E,
kControl6 = 0x1F,
kFeedbackControl = 0x1A,
kControl1 = 0x1B,
kControl2 = 0x1C,
kControl3 = 0x1D,
kControl4 = 0x1E,
kControl5 = 0x1F,
kSupplyVoltageMonitor = 0x21, // "VBAT"
kLraResonancePeriod = 0x22,
@ -295,12 +309,12 @@ class Haptics {
kOverdriveClampVoltage = 0x8C,
kAutoCalibrationCompensationResult = 0x0C,
kAutoCalibrationBackEmfResult = 0x6C,
kControl1 = 0x36,
kControl2 = 0x93,
kControl3 = 0xF5,
kControl4 = 0xA0,
kControl5 = 0x20,
kControl6 = 0x80,
kFeedbackControl = 0x36,
kControl1 = 0x93,
kControl2 = 0xF5,
kControl3 = 0xA0,
kControl4 = 0x20,
kControl5 = 0x80,
kSupplyVoltageMonitor = 0,
kLraResonancePeriod = 0,
};

@ -68,6 +68,7 @@ class NvsStorage {
auto Read() -> void;
auto Write() -> bool;
// Hardware Compatibility
auto LockPolarity() -> bool;
auto LockPolarity(bool) -> void;
@ -76,6 +77,10 @@ class NvsStorage {
auto DisplaySize(std::pair<std::optional<uint16_t>, std::optional<uint16_t>>)
-> void;
auto HapticMotorIsErm() -> bool;
auto HapticMotorIsErm(bool) -> void;
// /Hardware Compatibility
auto PreferredBluetoothDevice() -> std::optional<bluetooth::MacAndName>;
auto PreferredBluetoothDevice(std::optional<bluetooth::MacAndName>) -> void;
@ -130,6 +135,7 @@ class NvsStorage {
Setting<uint8_t> lock_polarity_;
Setting<uint16_t> display_cols_;
Setting<uint16_t> display_rows_;
Setting<uint8_t> haptic_motor_type_;
Setting<uint8_t> brightness_;
Setting<uint8_t> sensitivity_;

@ -39,6 +39,7 @@ static constexpr char kKeyScrollSensitivity[] = "scroll";
static constexpr char kKeyLockPolarity[] = "lockpol";
static constexpr char kKeyDisplayCols[] = "dispcols";
static constexpr char kKeyDisplayRows[] = "disprows";
static constexpr char kKeyHapticMotorType[] = "hapticmtype";
static constexpr char kKeyDbAutoIndex[] = "dbautoindex";
static auto nvs_get_string(nvs_handle_t nvs, const char* key)
@ -166,6 +167,7 @@ NvsStorage::NvsStorage(nvs_handle_t handle)
lock_polarity_(kKeyLockPolarity),
display_cols_(kKeyDisplayCols),
display_rows_(kKeyDisplayRows),
haptic_motor_type_(kKeyHapticMotorType),
brightness_(kKeyBrightness),
sensitivity_(kKeyScrollSensitivity),
amp_max_vol_(kKeyAmpMaxVolume),
@ -188,6 +190,7 @@ auto NvsStorage::Read() -> void {
lock_polarity_.read(handle_);
display_cols_.read(handle_);
display_rows_.read(handle_);
haptic_motor_type_.read(handle_),
brightness_.read(handle_);
sensitivity_.read(handle_);
amp_max_vol_.read(handle_);
@ -205,6 +208,7 @@ auto NvsStorage::Write() -> bool {
lock_polarity_.write(handle_);
display_cols_.write(handle_);
display_rows_.write(handle_);
haptic_motor_type_.write(handle_),
brightness_.write(handle_);
sensitivity_.write(handle_);
amp_max_vol_.write(handle_);
@ -243,6 +247,16 @@ auto NvsStorage::LockPolarity(bool p) -> void {
lock_polarity_.set(p);
}
auto NvsStorage::HapticMotorIsErm() -> bool {
std::lock_guard<std::mutex> lock{mutex_};
return haptic_motor_type_.get().value_or(0) > 0;
}
auto NvsStorage::HapticMotorIsErm(bool p) -> void {
std::lock_guard<std::mutex> lock{mutex_};
haptic_motor_type_.set(p);
}
auto NvsStorage::DisplaySize()
-> std::pair<std::optional<uint16_t>, std::optional<uint16_t>> {
std::lock_guard<std::mutex> lock{mutex_};

@ -34,6 +34,13 @@ static auto samd(lua_State* L) -> int {
return 1;
}
static auto update_samd(lua_State* L) -> int {
Bridge* instance = Bridge::Get(L);
auto& samd = instance->services().samd();
samd.ResetToFlashSamd();
return 0;
}
static auto collator(lua_State* L) -> int {
Bridge* instance = Bridge::Get(L);
auto& collator = instance->services().collator();
@ -45,6 +52,7 @@ static auto collator(lua_State* L) -> int {
static const struct luaL_Reg kVersionFuncs[] = {{"esp", esp},
{"samd", samd},
{"collator", collator},
{"update_samd", update_samd},
{NULL, NULL}};
static auto lua_version(lua_State* L) -> int {

@ -62,6 +62,10 @@ auto Booting::entry() -> void {
sServices->nvs(
std::unique_ptr<drivers::NvsStorage>(drivers::NvsStorage::OpenSync()));
// HACK: tell the unit that it has an ERM motor (we will likely default to
// LRAs in future, but all the current units in the field use ERMs.)
sServices->nvs().HapticMotorIsErm(true);
// HACK: fix up the switch polarity on newer dev units
// sServices->nvs().LockPolarity(false);
@ -85,7 +89,11 @@ auto Booting::entry() -> void {
sServices->samd(std::unique_ptr<drivers::Samd>(drivers::Samd::Create()));
sServices->touchwheel(
std::unique_ptr<drivers::TouchWheel>{drivers::TouchWheel::Create()});
sServices->haptics(std::make_unique<drivers::Haptics>());
sServices->haptics(std::make_unique<drivers::Haptics>(
sServices->nvs().HapticMotorIsErm()
? std::variant<drivers::ErmMotor, drivers::LraMotor>(
drivers::ErmMotor())
: drivers::LraMotor()));
auto adc = drivers::AdcBattery::Create();
sServices->battery(std::make_unique<battery::Battery>(

@ -5,7 +5,7 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
set(PROJECT_VER "0.8.2")
set(PROJECT_VER "0.8.3")
# esp-idf sets the C++ standard weird. Set cmake vars to match.
set(CMAKE_CXX_STANDARD 23)
@ -43,11 +43,11 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# Additional warnings used when compiling our components.
# Unable to be used due to issues in ESP-IDF includes are: -Wpedantic
# -Wuseless-cast -Wconversion -Wold-style-cast -Wsign-conversion -Wcast-align
set(EXTRA_WARNINGS "-Wshadow" "-Wnon-virtual-dtor" "-Wunused"
"-Woverloaded-virtual" "-Wmisleading-indentation" "-Wduplicated-cond"
"-Wduplicated-branches" "-Wlogical-op" "-Wnull-dereference"
"-Wdouble-promotion" "-Wformat=2" "-Wimplicit-fallthrough"
"-Wno-deprecated-enum-enum-conversion" "-Wno-array-bounds")
set(EXTRA_WARNINGS "-Wnon-virtual-dtor" "-Wunused" "-Woverloaded-virtual"
"-Wmisleading-indentation" "-Wduplicated-cond" "-Wduplicated-branches"
"-Wlogical-op" "-Wnull-dereference" "-Wdouble-promotion" "-Wformat=2"
"-Wimplicit-fallthrough" "-Wno-deprecated-enum-enum-conversion"
"-Wno-array-bounds" "-Wno-missing-field-initializers")
# Extra build flags that should apply to the entire build. This should mostly
# just be used to setting flags that our external dependencies requires.

Loading…
Cancel
Save