build: clean up the mess of ifdefs from platform into a scons-configured hal

This commit is contained in:
Torrie Fischer
2023-12-20 10:47:26 +01:00
parent 236795917a
commit 23993a09cf
21 changed files with 376 additions and 410 deletions

View File

@@ -0,0 +1,93 @@
#include "BluetoothSerialTelemetry.h"
#include "../../Static.h"
#include "../../Platform.h"
#include <ArduinoLog.h>
#include "../../inputs/Buttons.h"
#include <cstdlib>
BluetoothSerialTelemetry::BluetoothSerialTelemetry() : InputSource("Bluetooth")
{
//m_serial.setPin("0000");
m_serial.enableSSP();
}
InputEvent
BluetoothSerialTelemetry::read()
{
bool didRead = false;
while (m_serial.available()) {
didRead = true;
char charRead = m_serial.read();
m_ringbuf.insert(charRead);
if (charRead == '*') {
static char commandBuf[32];
size_t cmdSize = m_ringbuf.write(commandBuf);
// Overwrite the '*' character, to leave us with a complete command
commandBuf[cmdSize-1] = 0;
Log.verbose("Bluetooth read %s", commandBuf);
if (commandBuf[0] == 'R') {
m_color = CRGB(std::atoi(&commandBuf[1]), m_color.g, m_color.b);
return InputEvent{InputEvent::SetColor, m_color};
} else if (commandBuf[0] == 'G') {
m_color = CRGB(m_color.r, std::atoi(&commandBuf[1]), m_color.b);
return InputEvent{InputEvent::SetColor, m_color};
} else if (commandBuf[0] == 'B') {
m_color = CRGB(m_color.r, m_color.g, std::atoi(&commandBuf[1]));
return InputEvent{InputEvent::SetColor, m_color};
} else if (commandBuf[0] == 'O') {
return InputEvent{InputEvent::UserInput, Buttons::Circle};
} else if (commandBuf[0] == 'S') {
return InputEvent{InputEvent::UserInput, Buttons::Triangle};
} else if (commandBuf[0] == 'X') {
return InputEvent{InputEvent::UserInput, Buttons::Cross};
} else if (commandBuf[0] == '+') {
return InputEvent{InputEvent::SetPower, 1};
} else if (commandBuf[0] == '-') {
return InputEvent{InputEvent::SetPower, 0};
} else if (commandBuf[0] == 'p') {
return InputEvent{InputEvent::SetPattern, &commandBuf[1]};
} else if (commandBuf[0] == 'b') {
return InputEvent::BeatDetect;
} else if (commandBuf[0] == 'c') {
return InputEvent{InputEvent::SetDisplayLength, std::atoi(&commandBuf[1])};
} else if (commandBuf[0] == 'Y') {
return InputEvent::SaveConfigurationRequest;
} else if (commandBuf[0] == 'A') {
char* axisVal = strtok(&commandBuf[1], ",");
const uint8_t accelX = std::atof(axisVal) * 10;
axisVal = strtok(NULL, ",");
const uint8_t accelY = std::atof(axisVal) * 10;
axisVal = strtok(NULL, ",");
const uint8_t accelZ = std::atof(axisVal) * 10;
const uint16_t accelSum = abs(accelX) + abs(accelY) + abs(accelZ);
const uint16_t delta = abs(m_value.value() - accelSum);
m_value.add(accelSum);
if (delta > 32) {
return InputEvent{InputEvent::Acceleration, delta};
}
}
}
}
if (didRead) {
return InputEvent::NetworkActivity;
} else {
return InputEvent{};
}
}
void
BluetoothSerialTelemetry::onStart()
{
Log.trace("Starting up Bluetooth...");
if (m_serial.begin(Platform::deviceName())) {
Log.notice("Bluetooth started! Device name is %s", Platform::deviceName());
} else {
Log.warning("Bluetooth could not be started!");
}
}
STATIC_ALLOC(BluetoothSerialTelemetry);
STATIC_TASK(BluetoothSerialTelemetry);

View File

@@ -0,0 +1,41 @@
#include <Figments.h>
#include <BluetoothSerial.h>
#include <Ringbuf.h>
class BluetoothSerialTelemetry : public InputSource {
public:
BluetoothSerialTelemetry();
void onStart() override;
InputEvent read() override;
template<typename T, uint8_t Size = 8>
struct Averager {
std::array<T, Size> buf;
unsigned int idx = 0;
unsigned int count = 0;
void add(const T &value) {
buf[idx] = value;
idx = (idx + 1) % Size;
if (count < Size) {
count += 1;
}
}
T value() const {
if (count == 0) {
return T{};
}
long long int sum = 0;
for(unsigned int i = 0; i < count; i++) {
sum += buf[i];
}
return sum / count;
}
};
private:
BluetoothSerial m_serial;
Ringbuf<char, 32> m_ringbuf;
CRGB m_color;
Averager<int16_t, 32> m_value;
};

View File

@@ -0,0 +1,106 @@
#include "../../../Platform.h"
#include <WiFi.h>
#include <esp_task_wdt.h>
__NOINIT_ATTR static uint8_t s_rebootCount;
__NOINIT_ATTR static uint16_t s_forceSafeMode;
#define SAFE_MODE_MAGIC 6942
template<>
void
PlatformImpl<HAL_ESP32>::initBootOptions(BootOptions& opts)
{
opts.resetReason = esp_reset_reason();
opts.crashCount = s_rebootCount;
if (opts.resetReason >= 4) { // TODO: These values are defined in
// esp32/rom/rtc.h, but not sure if that's included
// on platformio builds
if (opts.crashCount++ >= 3) {
// Boot into safe mode if the watchdog reset us three times in a row.
isSafeMode = true;
}
} else {
opts.crashCount = 0;
}
s_rebootCount = crashCount;
}
template<>
const char*
PlatformImpl<HAL_ESP32>::name()
{
return "ESP32";
}
template<>
const char*
PlatformImpl<HAL_ESP32>::version()
{
return ESP.getSdkVersion();
}
template<>
const char*
PlatformImpl<HAL_ESP32>::deviceID()
{
uint64_t chipid;
chipid = ESP.getEfuseMac();
snprintf(s_deviceID, sizeof(s_deviceID), "%08X", (uint32_t)chipid);
return s_deviceID;
}
template<>
int
PlatformImpl<HAL_ESP32>::freeRam()
{
return ESP.getFreeHeap();
}
int printEspLog(const char* fmt, va_list args)
{
Log.notice(fmt, args);
return 1;
}
template<>
void
PlatformImpl<HAL_ESP32>::startWatchdog()
{
esp_task_wdt_init(10, true);
esp_task_wdt_add(NULL);
esp_log_set_vprintf(printEspLog);
}
template<>
void
PlatformImpl<HAL_ESP32>::startNTP()
{
constexpr int dst = 1;
configTime(0, 3600 * dst, "pool.ntp.org");
}
template<>
void
PlatformImpl<HAL_ESP32>::loop()
{
esp_task_wdt_reset();
}
template<>
bool
PlatformImpl<HAL_ESP32>::getLocalTime(struct tm* timedata, int timezone)
{
time_t rawtime;
memset(&rawtime, 0, sizeof(rawtime));
time(&rawtime);
(*timedata) = (*localtime(&rawtime));
return (timedata->tm_year > (2016-1990));
}
template<>
void
PlatformImpl<HAL_ESP32>::restart()
{
ESP.restart();
}

View File

@@ -0,0 +1,238 @@
#include <Figments.h>
#include <U8g2lib.h>
#include "../../Static.h"
#include <ArduinoLog.h>
#include "../../LogService.h"
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, 16, 15, 4);
class U8Display : public Task {
public:
U8Display() : Task("U8Display") {}
enum ScreenState {
BootSplash,
Running,
Message,
Idle = Running
};
void onStart() {
Log.trace("display: starting redraw thread");
xTaskCreatePinnedToCore(
&U8Display::redrawTask,
name,
2000,
this,
1,
&m_renderTask, 0
);
}
void onStop() {
Log.trace("display: stopping redraw thread");
vTaskDelete(m_renderTask);
}
void handleEvent(const InputEvent& evt) {
m_lastEvent = evt;
if (m_state == Idle) {
switch(evt.intent) {
case InputEvent::NetworkStatus:
m_nextState = Message;
m_message = evt.asBool() ? "Online!" : "Offline :[";
break;
case InputEvent::SetPattern:
m_nextState = Message;
m_message = "Pattern: " + String(evt.asString());
break;
case InputEvent::SetPower:
m_nextState = Message;
m_message = evt.asBool() ? "Power On" : "Power Off";
break;
case InputEvent::SaveConfigurationRequest:
m_nextState = Message;
m_message = "Settings Saved!";
break;
case InputEvent::FirmwareUpdate:
m_nextState = Message;
m_message = "Firmware update";
break;
case InputEvent::PreviousPattern:
m_nextState = Message;
m_message = "Previous Pattern";
break;
case InputEvent::NextPattern:
m_nextState = Message;
m_message = "Next Pattern";
break;
}
}
}
void drawSplash() {
//u8g2.setFont(u8g2_font_VCR_OSD_mr);
u8g2.setFont(u8g2_font_HelvetiPixelOutline_tr);
u8g2.setDrawColor(1);
uint8_t splashWidth = u8g2.getStrWidth("Renderbug!");
u8g2.drawStr(64 - splashWidth / 2, 32 - 15, "Renderbug!");
u8g2.setFont(u8g2_font_7x13B_mr);
u8g2.setCursor(15, 64 - 7);
u8g2.print("Version ");
u8g2.print(RENDERBUG_VERSION);
for(int i = 0; i < 3; i++) {
uint8_t sparkleX = (64 - splashWidth/2) + scale8(7-i, beatsin8(40)) * (splashWidth/7) + 5;
uint8_t sparkleY = scale8(3+i, beatsin8(40)) * 3 + 7;
u8g2.setDrawColor(2);
if (beatsin8(60*4) + i * 3 >= 170) {
u8g2.drawLine(sparkleX - 3, sparkleY - 3, sparkleX + 3, sparkleY + 3);
u8g2.drawLine(sparkleX + 3, sparkleY - 3, sparkleX - 3, sparkleY + 3);
} else if (beatsin8(60*4) + i * 2 >= 82) {
u8g2.drawLine(sparkleX - 4, sparkleY - 4, sparkleX + 2, sparkleY + 2);
u8g2.drawLine(sparkleX - 2, sparkleY - 2, sparkleX + 4, sparkleY + 4);
u8g2.drawLine(sparkleX + 4, sparkleY - 4, sparkleX - 2, sparkleY + 2);
u8g2.drawLine(sparkleX + 2, sparkleY - 2, sparkleX - 4, sparkleY + 4);
} else {
u8g2.drawLine(sparkleX - 2, sparkleY, sparkleX + 2, sparkleY);
u8g2.drawLine(sparkleX, sparkleY - 2, sparkleX, sparkleY + 2);
}
}
}
void drawMessage() {
//u8g2.setFont(u8g2_font_VCR_OSD_mr);
u8g2.setFont(u8g2_font_HelvetiPixelOutline_tr);
uint8_t splashWidth = u8g2.getStrWidth(m_message.c_str());
if (splashWidth >= 128) {
u8g2.setFont(u8g2_font_7x13B_mr);
splashWidth = u8g2.getStrWidth(m_message.c_str());
}
u8g2.drawStr(64 - splashWidth / 2, 32 - 15, m_message.c_str());
}
void drawTime() {
u8g2.setFont(u8g2_font_7x13O_tn);
u8g2.setCursor(0, 64);
struct tm timeinfo;
Platform::getLocalTime(&timeinfo);
uint8_t hour = timeinfo.tm_hour;
uint8_t minute = timeinfo.tm_min;
u8g2.print(hour);
u8g2.print(":");
u8g2.print(minute);
}
void drawState(ScreenState state) {
switch(state) {
case BootSplash:
drawSplash();
break;
case Message:
drawMessage();
break;
case Running:
uint8_t y = 11;
u8g2.setFont(u8g2_font_7x13B_mr);
u8g2.setCursor(0, y);
u8g2.print("FPS: ");
u8g2.setFont(u8g2_font_7x13O_tn);
u8g2.print(FastLED.getFPS());
y += 12;
u8g2.setCursor(0, y);
u8g2.setFont(u8g2_font_7x13B_mr);
u8g2.print("Last event: ");
y += 7;
u8g2.setCursor(10, y);
u8g2.setFont(u8g2_font_4x6_tr);
const char* intentName = LogService::intentName(m_lastEvent.intent);
if (intentName) {
u8g2.print(intentName);
} else {
u8g2.print("<");
u8g2.print(m_lastEvent.intent);
u8g2.print(">");
}
y += 12;
u8g2.setCursor(15, y);
u8g2.setFont(u8g2_font_7x13O_tf);
u8g2.print(LogService::eventValue(m_lastEvent));
drawTime();
break;
}
}
void loop() {
EVERY_N_MILLISECONDS(8) {
xTaskNotifyGive(m_renderTask);
}
}
private:
ScreenState m_state = BootSplash;
ScreenState m_nextState = BootSplash;
uint8_t m_transitionFrame = 0;
uint8_t m_screenStartTime = 0;
InputEvent m_lastEvent;
String m_message;
TaskHandle_t m_renderTask;
void redraw() {
if (m_state != m_nextState) {
EVERY_N_MILLISECONDS(8) {
constexpr uint8_t speed = 11;
if (m_transitionFrame <= 255 - speed) {
m_transitionFrame += speed;
} else {
m_transitionFrame = 255;
}
uint8_t offset = ease8InOutApprox(m_transitionFrame);
u8g2.clearBuffer();
if (m_transitionFrame <= 128) {
uint8_t width = scale8(128, offset * 2);
u8g2.setDrawColor(1);
drawState(m_state);
u8g2.drawBox(0, 0, width, 64);
u8g2.setDrawColor(2);
u8g2.drawBox(width, 0, 8, 64);
} else {
uint8_t width = scale8(128, offset/2)*2;
u8g2.setDrawColor(1);
drawState(m_nextState);
u8g2.setDrawColor(2);
u8g2.drawBox(std::max(0, width - 8), 0, 8, 64);
u8g2.setDrawColor(1);
u8g2.drawBox(width, 0, 128, 64);
}
u8g2.sendBuffer();
if (m_transitionFrame == 255) {
m_state = m_nextState;
m_screenStartTime = millis();
m_transitionFrame = 0;
}
}
} else {
u8g2.clearBuffer();
drawState(m_state);
u8g2.sendBuffer();
uint16_t screenTime = millis() - m_screenStartTime;
if (screenTime >= 7000 && m_state != Idle) {
m_nextState = Idle;
}
}
}
static void redrawTask(void* data) {
u8g2.begin();
U8Display* self = static_cast<U8Display*>(data);
self->m_screenStartTime = millis();
while (true) {
self->redraw();
ulTaskNotifyTake(0, portMAX_DELAY);
}
}
};
STATIC_ALLOC(U8Display);
STATIC_TASK(U8Display);