Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RGB Support plus OpenShock Core V1 board definition #121

Merged
merged 10 commits into from
Nov 25, 2023
Merged
16 changes: 16 additions & 0 deletions chips/ESP32-S3/N8/merge-image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/python3

import esptool

# fmt: off
# Note: Bootloader for esp32-s3 starts at 0x0000, unlike several other ESP32 variants that start at 0x1000.
esptool.main([
'--chip', 'esp32s3',
'merge_bin', '-o', 'merged.bin',
'--flash_size', '8MB',
'0x0', './bootloader.bin',
'0x8000', './partitions.bin',
'0x10000', './firmware.bin',
'0x310000', './filesystem.bin'
])
# fmt: on
6 changes: 6 additions & 0 deletions chips/ESP32-S3/N8/partitions.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x300000,
spiffs, data, spiffs, 0x310000,0xE0000,
coredump, data, coredump,0x3F0000,0x10000,
43 changes: 43 additions & 0 deletions include/RGBPatternManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once

#include <nonstd/span.hpp>

#include <esp32-hal-rmt.h>

#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>

#include <cstdint>

namespace OpenShock {
class RGBPatternManager {
public:
RGBPatternManager(std::uint8_t gpioPin);
~RGBPatternManager();

struct RGBState {
std::uint8_t red;
std::uint8_t green;
std::uint8_t blue;
std::uint32_t duration;
};

void SetPattern(nonstd::span<const RGBState> pattern);
void SetBrightness(std::uint8_t brightness);
void ClearPattern();

private:
void ClearPatternInternal();
void SendRGB(const RGBState state);
static void RunPattern(void* arg);

std::uint8_t m_rgbPin;
std::uint8_t m_rgbBrightness; // 0-255
rmt_obj_t* m_rmtHandle;
RGBState* m_pattern;
std::size_t m_patternLength;
TaskHandle_t m_taskHandle;
SemaphoreHandle_t m_taskSemaphore;
};
} // namespace OpenShock
3 changes: 2 additions & 1 deletion include/VisualStateManager.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#pragma once

#include "EStopManager.h"
#include <cstdint>

namespace OpenShock::VisualStateManager {
void Init();

void SetCriticalError();
void SetScanningStarted();
void SetEmergencyStop(bool isStopped);
void SetEmergencyStop(OpenShock::EStopManager::EStopStatus status);
void SetWebSocketConnected(bool isConnected);
} // namespace OpenShock::VisualStateManager
14 changes: 13 additions & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ lib_deps =
https://github.com/martinmoene/span-lite
https://github.com/OpenShock/ESPAsyncWebServer
https://github.com/Links2004/arduinoWebSockets
board_build.partitions = huge_app.csv ; Overridden per board
board_build.partitions = huge_app.csv ; Overridden per board by custom_openshock selections
board_build.filesystem = littlefs
board_build.embed_files = certificates/x509_crt_bundle
extra_scripts =
Expand Down Expand Up @@ -87,5 +87,17 @@ custom_openshock.chip_variant = N8R8
build_flags =
-DOPENSHOCK_LED_GPIO=21

; https://github.com/nullstalgia/OpenShock-Hardware/tree/main/Core
; 8MB Flash, assume no PSRAM.
[env:OpenShock-Core-V1]
board = esp32-s3-devkitc-1 ; builtin
custom_openshock.chip = ESP32-S3
custom_openshock.chip_variant = N8
build_flags =
-DOPENSHOCK_LED_WS2812B=48
-DOPENSHOCK_LED_GPIO=35
hhvrc marked this conversation as resolved.
Show resolved Hide resolved
-DOPENSHOCK_TX_PIN=15
-DOPENSHOCK_ESTOP_PIN=13

; TODO:
; https://docs.platformio.org/en/latest/boards/espressif32/upesy_wroom.html;upesy-esp32-wroom-devkit
18 changes: 10 additions & 8 deletions src/EStopManager.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "EStopManager.h"

#include "Time.h"
#include "Logging.h"
#include "Time.h"
#include "VisualStateManager.h"

#include <Arduino.h>
Expand All @@ -10,11 +10,11 @@ const char* const TAG = "EStopManager";

using namespace OpenShock;

static EStopManager::EStopStatus s_estopStatus = EStopManager::EStopStatus::ALL_CLEAR;
static std::uint32_t s_estopHoldToClearTime = 5000;
static std::int64_t s_lastEStopButtonStateChange = 0;
static std::int64_t s_estoppedAt = 0;
static bool s_lastEStopButtonState = HIGH;
static EStopManager::EStopStatus s_estopStatus = EStopManager::EStopStatus::ALL_CLEAR;
static std::uint32_t s_estopHoldToClearTime = 5000;
static std::int64_t s_lastEStopButtonStateChange = 0;
static std::int64_t s_estoppedAt = 0;
static bool s_lastEStopButtonState = HIGH;

static std::uint8_t s_estopPin;

Expand Down Expand Up @@ -61,28 +61,30 @@ EStopManager::EStopStatus EStopManager::Update() {
s_estopStatus = EStopManager::EStopStatus::ESTOPPED_AND_HELD;
s_estoppedAt = s_lastEStopButtonStateChange;
ESP_LOGI(TAG, "Emergency Stopped!!!");
OpenShock::VisualStateManager::SetEmergencyStop(true);
OpenShock::VisualStateManager::SetEmergencyStop(s_estopStatus);
}
break;
case EStopManager::EStopStatus::ESTOPPED_AND_HELD:
if (buttonState == HIGH) {
// User has released the button, now we can trust them holding to clear it.
s_estopStatus = EStopManager::EStopStatus::ESTOPPED;
OpenShock::VisualStateManager::SetEmergencyStop(s_estopStatus);
}
break;
case EStopManager::EStopStatus::ESTOPPED:
// If the button is held again for the specified time after being released, clear the EStop
if (buttonState == LOW && s_lastEStopButtonState == LOW && s_lastEStopButtonStateChange + s_estopHoldToClearTime <= OpenShock::millis()) {
s_estopStatus = EStopManager::EStopStatus::ESTOPPED_CLEARED;
ESP_LOGI(TAG, "Clearing EStop on button release!");
OpenShock::VisualStateManager::SetEmergencyStop(false);
OpenShock::VisualStateManager::SetEmergencyStop(s_estopStatus);
}
break;
case EStopManager::EStopStatus::ESTOPPED_CLEARED:
// If the button is released, report as ALL_CLEAR
if (buttonState == HIGH) {
s_estopStatus = EStopManager::EStopStatus::ALL_CLEAR;
ESP_LOGI(TAG, "All clear!");
OpenShock::VisualStateManager::SetEmergencyStop(s_estopStatus);
}
break;

Expand Down
134 changes: 134 additions & 0 deletions src/RGBPatternManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#include "RGBPatternManager.h"

#include "Logging.h"
#include "util/TaskUtils.h"

#include <Arduino.h>

const char* const TAG = "RGBPatternManager";

using namespace OpenShock;

// Currently this assumes a single WS2812B LED
// TODO: Support multiple LEDs ?
// TODO: Support other LED types ?

RGBPatternManager::RGBPatternManager(std::uint8_t rgbPin) : m_rgbPin(rgbPin), m_rgbBrightness(255), m_pattern(nullptr), m_patternLength(0), m_taskHandle(nullptr), m_taskSemaphore(xSemaphoreCreateBinary()) {
if ((m_rmtHandle = rmtInit(m_rgbPin, RMT_TX_MODE, RMT_MEM_64)) == NULL) {
ESP_LOGE(TAG, "[pin-%u] Failed to create rgb rmt object", m_rgbPin);
}

float realTick = rmtSetTick(m_rmtHandle, 100);
ESP_LOGD(TAG, "[pin-%u] real tick set to: %fns", m_rgbPin, realTick);

SetBrightness(20);

xSemaphoreGive(m_taskSemaphore);
}

RGBPatternManager::~RGBPatternManager() {
ClearPattern();

vSemaphoreDelete(m_taskSemaphore);
}

void RGBPatternManager::SetPattern(nonstd::span<const RGBState> pattern) {
ClearPatternInternal();

// Set new values
m_patternLength = pattern.size();
m_pattern = new RGBState[m_patternLength];
std::copy(pattern.begin(), pattern.end(), m_pattern);

// Start the task
BaseType_t result = TaskUtils::TaskCreateExpensive(RunPattern, TAG, 4096, this, 1, &m_taskHandle);
if (result != pdPASS) {
ESP_LOGE(TAG, "[pin-%u] Failed to create task: %d", m_rgbPin, result);

m_taskHandle = nullptr;

if (m_pattern != nullptr) {
delete[] m_pattern;
m_pattern = nullptr;
}
m_patternLength = 0;
}

// Give the semaphore back
xSemaphoreGive(m_taskSemaphore);
}

void RGBPatternManager::ClearPattern() {
ClearPatternInternal();
xSemaphoreGive(m_taskSemaphore);
}

void RGBPatternManager::ClearPatternInternal() {
xSemaphoreTake(m_taskSemaphore, portMAX_DELAY);

if (m_taskHandle != nullptr) {
vTaskDelete(m_taskHandle);
m_taskHandle = nullptr;
}

if (m_pattern != nullptr) {
delete[] m_pattern;
m_pattern = nullptr;
}
m_patternLength = 0;
}

// Range: 0-255
void RGBPatternManager::SetBrightness(std::uint8_t brightness) {
m_rgbBrightness = brightness;
}

void RGBPatternManager::SendRGB(const RGBState state) {
const std::uint16_t bitCount = (8 * 3) * (1); // 8 bits per color * 3 colors * 1 LED

rmt_data_t led_data[bitCount];

std::uint8_t r = (std::uint8_t)((std::uint16_t)state.red * m_rgbBrightness / 255);
std::uint8_t g = (std::uint8_t)((std::uint16_t)state.green * m_rgbBrightness / 255);
std::uint8_t b = (std::uint8_t)((std::uint16_t)state.blue * m_rgbBrightness / 255);

std::uint8_t led, col, bit;
std::uint8_t i = 0;
// WS2812B takes commands in GRB order
// https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf - Page 5
std::uint8_t colors[3] = {g, r, b};
for (led = 0; led < 1; led++) {
for (col = 0; col < 3; col++) {
for (bit = 0; bit < 8; bit++) {
if ((colors[col] & (1 << (7 - bit)))) {
led_data[i].level0 = 1;
led_data[i].duration0 = 8;
led_data[i].level1 = 0;
led_data[i].duration1 = 4;
} else {
led_data[i].level0 = 1;
led_data[i].duration0 = 4;
led_data[i].level1 = 0;
led_data[i].duration1 = 8;
}
i++;
}
}
}
// Send the data
rmtWriteBlocking(m_rmtHandle, led_data, bitCount);
}

void RGBPatternManager::RunPattern(void* arg) {
RGBPatternManager* thisPtr = reinterpret_cast<RGBPatternManager*>(arg);

RGBPatternManager::RGBState* pattern = thisPtr->m_pattern;
std::size_t patternLength = thisPtr->m_patternLength;

while (true) {
for (std::size_t i = 0; i < patternLength; ++i) {
thisPtr->SendRGB(pattern[i]);
vTaskDelay(pdMS_TO_TICKS(pattern[i].duration));
}
}
}
Loading