Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,322 changes: 0 additions & 1,322 deletions Data/system/app/WebServer/dashboard.html

This file was deleted.

3 changes: 0 additions & 3 deletions Data/system/app/WebServer/version.json

This file was deleted.

1 change: 0 additions & 1 deletion Devices/heltec-wifi-lora-32-v3/device.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
general.vendor=Heltec
general.name=WiFi LoRa 32 v3
general.incubating=true
apps.autoStartAppId=tactility.apwebserver

hardware.target=ESP32S3
hardware.flashSize=8MB
Expand Down
2 changes: 1 addition & 1 deletion Devices/lilygo-tdisplay-s3/device.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
general.vendor=LilyGO
general.name=T-Display S3
apps.autoStartAppId=tactility.apwebserver

hardware.target=ESP32S3
hardware.flashSize=16MB
Expand All @@ -17,3 +16,4 @@ display.size=1.9"
display.dpi=191

lvgl.colorDepth=16
lvgl.uiDensity=compact
2 changes: 0 additions & 2 deletions Devices/lilygo-tdisplay/device.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
general.vendor=LilyGO
general.name=T-Display
general.incubating=false
apps.autoStartAppId=tactility.apwebserver

hardware.target=ESP32
hardware.flashSize=16MB
Expand Down
1 change: 0 additions & 1 deletion Devices/lilygo-tdongle-s3/device.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
general.vendor=LilyGO
general.name=T-Dongle S3
general.incubating=false
apps.autoStartAppId=tactility.apwebserver

hardware.target=ESP32S3
hardware.flashSize=16MB
Expand Down
5 changes: 5 additions & 0 deletions Devices/lilygo-tdongle-s3/lilygo,tdongle-s3.dts
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,9 @@
compatible = "espressif,esp32-usbdevice-msc";
};
};

buttons {
compatible = "tactility,button-control";
pin-primary = <&gpio0 0 GPIO_FLAG_NONE>;
};
};
1 change: 1 addition & 0 deletions Devices/lilygo-tdongle-s3/module.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
dependencies:
- Platforms/platform-esp32
- Drivers/st7735-module
- Drivers/button-control-module
dts: lilygo,tdongle-s3.dts
dts-only: true
1 change: 0 additions & 1 deletion Devices/waveshare-esp32-s3-geek/device.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
general.vendor=Waveshare
general.name=ESP32 S3 GEEK
general.incubating=false
apps.autoStartAppId=tactility.apwebserver

hardware.target=ESP32S3
hardware.flashSize=16MB
Expand Down
3 changes: 1 addition & 2 deletions Devices/waveshare-s3-lcd-13/device.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
general.vendor=WaveShare
general.name=S3 LCD 1.3"
general.incubating=false
apps.autoStartAppId=tactility.apwebserver
general.incubating=true

hardware.target=ESP32S3
hardware.flashSize=16MB
Expand Down
6 changes: 3 additions & 3 deletions Devices/waveshare-s3-lcd-13/module.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
dependencies:
- Platforms/platform-esp32
- Drivers/qmi8658-module
- Drivers/st7789-module
- Platforms/platform-esp32
- Drivers/qmi8658-module
- Drivers/st7789-module
dts: waveshare,s3-lcd-13.dts
dts-only: true
19 changes: 19 additions & 0 deletions Devices/waveshare-s3-touch-amoled-18/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
file(GLOB_RECURSE SOURCE_FILES source/*.c*)

idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "source"
REQUIRES TactilityKernel Tactility TactilityFreeRtos lvgl-module lvgl-window-manager-module esp_driver_i2c esp_driver_gpio
)

# Board-local link-time hooks (see source/lvgl_scaling.cpp and source/lvgl_insets.cpp): keep the
# Material theme's widget sizes usable while tightening its spacing, and keep the UI clear of the
# top/bottom edges. No core Tactility/LVGL source is modified.
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=window_manager_configure" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_display_create" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_style_set_pad_top" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_style_set_pad_bottom" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_style_set_pad_left" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_style_set_pad_right" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_style_set_pad_row" APPEND)
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=lv_style_set_pad_column" APPEND)
30 changes: 30 additions & 0 deletions Devices/waveshare-s3-touch-amoled-18/device.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
general.vendor=Waveshare
general.name=S3 Touch AMOLED 1.8"
general.incubating=true

apps.launcherAppId=tactility.launcher

hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=120M
hardware.tinyUsbMsc=true
hardware.esptoolFlashFreq=120M
hardware.bluetooth=true

storage.userDataLocation=Internal

display.size=1.8"
display.shape=rectangle
display.dpi=322

lvgl.colorDepth=16
lvgl.uiDensity=default
lvgl.fontSize=22

sdkconfig.CONFIG_TASK_WDT_TIMEOUT_S=10
sdkconfig.CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=131072
# LVGL v9 symbol (v8 was LV_DISP_DEF_REFR_PERIOD). Upstream moved this out of device.py,
# so pin it per-device: 10 ms keeps the panel above the 16 ms animation tick.
sdkconfig.CONFIG_LV_DEF_REFR_PERIOD=10
11 changes: 11 additions & 0 deletions Devices/waveshare-s3-touch-amoled-18/module.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
dependencies:
- Platforms/platform-esp32
- Drivers/sh8601-module
- Drivers/axp2101-module
- Drivers/pcf85063-module
- Drivers/qmi8658-module
- Drivers/ft5x06-module
- Drivers/es8311-module
- Drivers/audio-stream-module
- Drivers/tca9534-module
dts: waveshare,s3-touch-amoled-18.dts
225 changes: 225 additions & 0 deletions Devices/waveshare-s3-touch-amoled-18/source/bluetooth_reconnect.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
// SPDX-License-Identifier: Apache-2.0

// Board-local Bluetooth auto-reconnect for the Waveshare S3 Touch AMOLED 1.8.
//
// Tactility's own auto-connect runs on the "bt-events" thread
// (Tactility/Source/bluetooth/Bluetooth.cpp). That thread can be blocked while the radio is on,
// so saved peers (e.g. a BLE keyboard) never get reconnected. This task subscribes to the kernel
// BT driver directly - its own subscription and its own thread, so it doesn't depend on bt-events
// - and, whenever the radio is on, scans for and reconnects every saved peer it sees.

#include <sdkconfig.h>

#if defined(CONFIG_BT_NIMBLE_ENABLED)

#include <Tactility/Thread.h>
#include <Tactility/bluetooth/Bluetooth.h>
#include <Tactility/bluetooth/BluetoothPairedDevice.h>

#include <tactility/concurrent/task_event_group.h>
#include <tactility/device.h>
#include <tactility/drivers/bluetooth.h>
#include <tactility/error.h>
#include <tactility/log.h>

#include <array>
#include <atomic>
#include <cstring>
#include <memory>
#include <vector>

namespace tt::bluetooth {

// The core HID host resolves a peer's address type from a scan cache that is filled in by the
// (blocked) bt-events thread. The helper that fills it is internal to Tactility (declared in
// Private/Tactility/bluetooth/BluetoothPrivate.h, which isn't exported to components), so it is
// re-declared here to prime that cache with the address type this task learns from its own scan.
void cacheScanAddr(const uint8_t addr[6], uint8_t addr_type);

} // namespace tt::bluetooth

namespace {

constexpr auto* TAG = "WsBtReconnect";

// How long the task blocks per wait, and how many of those slices make up one periodic retry.
// The slice bounds how quickly stop() is noticed; the total paces the retry scan.
constexpr TickType_t WAIT_SLICE = pdMS_TO_TICKS(500);
constexpr int RETRY_SLICES = 40; // ~20 s
// This task runs the same heavy work the core dispatches to the main task (settings::loadAll()
// plus tt::bluetooth::connect() -> hidHostConnect()), so it needs at least the main task's stack
// (CONFIG_ESP_MAIN_TASK_STACK_SIZE, 6144). Sized to match NimBLE's host task.
constexpr size_t TASK_STACK_SIZE = 8192;

// The advertised name must fit the 31-byte legacy advertising payload (<= 26 chars alongside the
// advertising flags). "S3 Touch AMOLED 1.8" fits, but it is pinned here so a change to the (longer)
// UI device name can't silently break advertising.
constexpr const char* ADVERTISED_NAME = "S3 Touch AMOLED 1.8";

std::atomic<bool> reconnect_running { false };
std::unique_ptr<tt::Thread> reconnect_thread;

struct SavedPeer {
std::array<uint8_t, 6> addr;
int profile_id;
bool attempted;
};

// Only ever touched by the task.
std::vector<SavedPeer> saved_peers;
Device* bt_device = nullptr;

void refresh_saved_peers() {
saved_peers.clear();
for (const auto& peer : tt::bluetooth::settings::loadAll()) {
saved_peers.push_back(SavedPeer { .addr = peer.addr, .profile_id = peer.profileId, .attempted = false });
}
}

SavedPeer* find_saved_peer(const BtAddr addr) {
for (auto& peer : saved_peers) {
if (std::memcmp(peer.addr.data(), addr, 6) == 0) {
return &peer;
}
}
return nullptr;
}

void connect_peer(const BtPeerRecord& record) {
SavedPeer* peer = find_saved_peer(record.addr);
if (peer == nullptr || peer->attempted) {
return;
}
peer->attempted = true;

// Prime the core HID host cache with the address type from our own scan.
tt::bluetooth::cacheScanAddr(record.addr, record.addr_type);

const auto hex = tt::bluetooth::settings::addrToHex(peer->addr);
LOG_I(TAG, "Reconnecting saved peer %s (profile=%d)", hex.c_str(), peer->profile_id);
tt::bluetooth::connect(peer->addr, peer->profile_id);
}

void scan_if_idle() {
if (bt_device == nullptr || saved_peers.empty()) {
return;
}
if (!tt::bluetooth::isRadioOnOrPending(bt_device)) {
return;
}
if (bluetooth_is_scanning(bt_device)) {
return;
}
for (auto& peer : saved_peers) {
peer.attempted = false;
}
bluetooth_scan_start(bt_device);
}

int32_t task_main() {
TaskEventGroup event_group {};
task_event_group_construct(&event_group);

// Wait for the Bluetooth device to be constructed and started (the module starts before the
// devicetree's devices do).
while (reconnect_running.load()) {
if (device_get_first_active_by_type(&BLUETOOTH_TYPE, &bt_device) == ERROR_NONE) {
break;
}
bt_device = nullptr;
vTaskDelay(pdMS_TO_TICKS(500));
}

BtEventSubscription subscription {};
if (!reconnect_running.load() || bt_device == nullptr ||
bluetooth_event_subscribe(bt_device, &subscription, &event_group) != ERROR_NONE) {
if (bt_device != nullptr) {
device_put(bt_device);
bt_device = nullptr;
}
task_event_group_destruct(&event_group);
return 0;
}

// Advertise a name that fits the legacy advertising payload (see ADVERTISED_NAME). The driver
// restarts advertising with it if the radio is already on.
if (bluetooth_set_device_name(bt_device, ADVERTISED_NAME) != ERROR_NONE) {
LOG_W(TAG, "Failed to set the advertised Bluetooth name");
}

refresh_saved_peers();
scan_if_idle();

int slices = 0;
while (reconnect_running.load()) {
task_event_group_wait_any(&event_group, nullptr, WAIT_SLICE);

BtEvent event {};
while (bluetooth_event_poll(&subscription, &event) == ERROR_NONE) {
switch (event.type) {
case BT_EVENT_RADIO_STATE_CHANGED:
if (event.radio_state == BT_RADIO_STATE_ON) {
refresh_saved_peers();
scan_if_idle();
}
break;
case BT_EVENT_SCAN_FINISHED:
LOG_I(TAG, "Scan finished");
break;
case BT_EVENT_PEER_FOUND:
connect_peer(event.peer);
break;
default:
break;
}
}

// Periodic retry: pick up newly paired peers and rescan when nothing connected yet.
if (++slices >= RETRY_SLICES) {
slices = 0;
refresh_saved_peers();
scan_if_idle();
}
}

bluetooth_event_unsubscribe(bt_device, &subscription);
device_put(bt_device);
bt_device = nullptr;
task_event_group_destruct(&event_group);
return 0;
}

void task_start() {
reconnect_thread = std::make_unique<tt::Thread>(
"ws-bt-reconnect",
TASK_STACK_SIZE,
[]() -> int32_t { return task_main(); }
);
reconnect_thread->start();
}

} // namespace

void waveshare_bluetooth_reconnect_start() {
if (reconnect_running.exchange(true)) {
return;
}
task_start();
}

void waveshare_bluetooth_reconnect_stop() {
if (!reconnect_running.exchange(false)) {
return;
}
if (reconnect_thread != nullptr) {
reconnect_thread->join();
reconnect_thread.reset();
}
}

#else

void waveshare_bluetooth_reconnect_start() {}
void waveshare_bluetooth_reconnect_stop() {}

#endif // CONFIG_BT_NIMBLE_ENABLED
14 changes: 14 additions & 0 deletions Devices/waveshare-s3-touch-amoled-18/source/bluetooth_reconnect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once

/**
* Board-local Bluetooth auto-reconnect for the Waveshare S3 Touch AMOLED 1.8.
*
* Starts a task that scans and reconnects saved (paired) BLE peers. It runs its own kernel BT
* event subscription on its own thread instead of relying on Tactility's "bt-events" thread,
* which can block while the radio is on.
*/
void waveshare_bluetooth_reconnect_start();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can't be merged. Should be fixed in the BT driver properly. I still need to refactor that driver though, which might help with this problem.


/** Stops the task, if running. */
void waveshare_bluetooth_reconnect_stop();
Loading
Loading