mirror of
https://github.com/BotChain-Robots/firmware.git
synced 2026-07-09 10:07:21 +02:00
OTA + remote logging + profiling
This commit is contained in:
@@ -17,7 +17,7 @@ else()
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${ALL_SRCS}
|
||||
PRIV_REQUIRES esp_psram spi_flash nvs_flash esp_event rpc constants config rmt esp_driver_gptimer dataLink flatbuffers esp_driver_ledc oled
|
||||
PRIV_REQUIRES esp_psram spi_flash nvs_flash esp_event rpc constants config rmt esp_driver_gptimer dataLink flatbuffers esp_driver_ledc oled app_update
|
||||
INCLUDE_DIRS "include")
|
||||
|
||||
if(DEFINED SRC_BOARD AND SRC_BOARD)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "LoopManager.h"
|
||||
#include "SensorMessageBuilder.h"
|
||||
#include "TopologyMessageBuilder.h"
|
||||
#include "esp_wifi.h"
|
||||
|
||||
#define ACTUATOR_CMD_TAG 5
|
||||
#define TOPOLOGY_CMD_TAG 6
|
||||
@@ -78,3 +79,7 @@ void LoopManager::send_sensor_reading(bool durable) const {
|
||||
m_messaging_interface->send((uint8_t *)ptr, size, PC_ADDR, SENSOR_TAG, durable);
|
||||
}
|
||||
}
|
||||
|
||||
MessagingInterface &LoopManager::get_messaging_interface() {
|
||||
return *m_messaging_interface;
|
||||
}
|
||||
|
||||
51
main/RemoteDebugging.cpp
Normal file
51
main/RemoteDebugging.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "RemoteDebugging.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
std::string RemoteDebugging::get_task_manager() {
|
||||
std::string out;
|
||||
out.resize(1024);
|
||||
vTaskGetRunTimeStats(out.data());
|
||||
out.resize(425);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string RemoteDebugging::get_logs() {
|
||||
std::string out;
|
||||
for (const auto& log : log_buffer->peek_drain()) {
|
||||
out.append(log);
|
||||
}
|
||||
out.resize(800);
|
||||
return out;
|
||||
}
|
||||
|
||||
void RemoteDebugging::register_calls(MessagingInterface &messaging_interface) {
|
||||
messaging_interface.register_function(2, [](const uint8_t *parameters, size_t parameter_size, Writer& writer) {
|
||||
auto out = get_task_manager();
|
||||
std::vector<uint8_t> vec(out.begin(), out.end());
|
||||
writer.write(vec);
|
||||
});
|
||||
messaging_interface.register_function(3, [](const uint8_t *parameters, size_t parameter_size, Writer& writer) {
|
||||
auto out = get_logs();
|
||||
std::vector<uint8_t> vec(out.begin(), out.end());
|
||||
writer.write(vec);
|
||||
});
|
||||
}
|
||||
|
||||
int RemoteDebugging::custom_log_write(const char *data, va_list args) {
|
||||
char tmp[512];
|
||||
|
||||
va_list copy;
|
||||
va_copy(copy, args);
|
||||
int len = vsnprintf(tmp, sizeof(tmp), data, copy);
|
||||
va_end(copy);
|
||||
|
||||
std::string str;
|
||||
str.assign(tmp, len);
|
||||
std::cout << str;
|
||||
log_buffer->push(std::move(str));
|
||||
|
||||
return 0;
|
||||
}
|
||||
82
main/RemoteManagement.cpp
Normal file
82
main/RemoteManagement.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "RemoteManagement.h"
|
||||
#include "OTAPacketBuilder.h"
|
||||
|
||||
#define TAG "RemoteManagement"
|
||||
|
||||
void RemoteManagement::mark_ota_success() {
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
esp_ota_img_states_t ota_state;
|
||||
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ...");
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
// ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ...");
|
||||
// esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RemoteManagement::start_ota() {
|
||||
ESP_LOGI(TAG, "Start OTA");
|
||||
return ESP_OK == esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &ota_handle);
|
||||
}
|
||||
|
||||
bool RemoteManagement::write_ota(const uint8_t* ota_data, size_t size) {
|
||||
|
||||
flatbuffers::Verifier verifier(ota_data, size);
|
||||
if (!Messaging::VerifyOTAPacketBuffer(verifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &ota_packet = Flatbuffers::OTAPacketBuilder::parse_ota_packet(ota_data);
|
||||
|
||||
if (ota_packet->sequence_number() <= packet_number) { // got an old packet
|
||||
return true;
|
||||
} else if (ota_packet->sequence_number() == packet_number + 1) { // got the next packet
|
||||
ESP_LOGI(TAG, "Write %d bytes of OTA packet %d", ota_packet->length(), ota_packet->sequence_number());
|
||||
if (ESP_OK == esp_ota_write(ota_handle, ota_packet->payload()->data(), ota_packet->length())) {
|
||||
packet_number++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false; // got an incorrect packet
|
||||
}
|
||||
|
||||
bool RemoteManagement::ota_end() {
|
||||
ESP_LOGI(TAG, "OTA end");
|
||||
if (ESP_OK != esp_ota_end(ota_handle)) {
|
||||
return false;
|
||||
}
|
||||
return ESP_OK == esp_ota_set_boot_partition(update_partition);
|
||||
}
|
||||
|
||||
void RemoteManagement::restart() {
|
||||
ESP_LOGI(TAG, "restart");
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
void RemoteManagement::register_calls(MessagingInterface &messaging_interface) {
|
||||
messaging_interface.register_function(4, [](const uint8_t *parameters, size_t parameter_size, Writer& writer) {
|
||||
const auto out = start_ota();
|
||||
ESP_LOGI(TAG, "Start returned");
|
||||
std::vector<uint8_t> vec{(uint8_t)(out ? 1 : 0)};
|
||||
writer.write(vec);
|
||||
});
|
||||
messaging_interface.register_function(5, [](const uint8_t *parameters, size_t parameter_size, Writer& writer) {
|
||||
const auto out = write_ota(parameters, parameter_size);
|
||||
std::vector<uint8_t> vec{(uint8_t)(out ? 1 : 0)};
|
||||
writer.write(vec);
|
||||
});
|
||||
messaging_interface.register_function(6, [](const uint8_t *parameters, size_t parameter_size, Writer& writer) {
|
||||
const auto out = ota_end();
|
||||
std::vector<uint8_t> vec{(uint8_t)(out ? 1 : 0)};
|
||||
writer.write(vec);
|
||||
});
|
||||
messaging_interface.register_function(7, [](const uint8_t *parameters, size_t parameter_size, Writer& writer) {
|
||||
restart();
|
||||
std::vector<uint8_t> vec{1};
|
||||
writer.write(vec);
|
||||
});
|
||||
}
|
||||
@@ -18,6 +18,10 @@ public:
|
||||
LoopManager() : m_config_manager(ConfigManager::get_instance()),
|
||||
m_messaging_interface(std::make_unique<MessagingInterface>()),
|
||||
m_actuator(ActuatorFactory::create_actuator(m_config_manager.get_module_type())) {
|
||||
xTaskCreate(reinterpret_cast<TaskFunction_t>(LoopManager::metadata_tx_loop),
|
||||
"metadata_tx", 3096, this, 3, nullptr);
|
||||
xTaskCreate(reinterpret_cast<TaskFunction_t>(LoopManager::sensor_loop),
|
||||
"sensor_tx", 3096, this, 3, nullptr);
|
||||
|
||||
// todo: temporary demo register_function call.
|
||||
const auto function_tag = 100;
|
||||
@@ -29,6 +33,7 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
MessagingInterface &get_messaging_interface();
|
||||
[[noreturn]] void control_loop() const; // gets control commands
|
||||
[[noreturn]] static void sensor_loop(char * args); // sends sensor data commands continually
|
||||
[[noreturn]] static void metadata_tx_loop(char * args); // sends metadata continually (low duty cycle)
|
||||
|
||||
26
main/include/RemoteDebugging.h
Normal file
26
main/include/RemoteDebugging.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef REMOTEDEBUGGING_H
|
||||
#define REMOTEDEBUGGING_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "MessagingInterface.h"
|
||||
#include "control/ActuatorFactory.h"
|
||||
#include "control/IActuator.h"
|
||||
#include "util/ring_buffer.h"
|
||||
|
||||
#define LOG_BUFFER_SIZE 50
|
||||
|
||||
static auto log_buffer = std::make_unique<RingBuffer<std::string>>(LOG_BUFFER_SIZE);
|
||||
|
||||
|
||||
class RemoteDebugging {
|
||||
public:
|
||||
static std::string get_task_manager();
|
||||
static std::string get_logs();
|
||||
static int custom_log_write(const char *data, va_list args);
|
||||
static void register_calls(MessagingInterface &messaging_interface);
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
#endif // REMOTEDEBUGGING_H
|
||||
26
main/include/RemoteManagement.h
Normal file
26
main/include/RemoteManagement.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef REMOTEMANAGEMENT_H
|
||||
#define REMOTEMANAGEMENT_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "esp_ota_ops.h"
|
||||
#include "MessagingInterface.h"
|
||||
|
||||
static esp_ota_handle_t ota_handle;
|
||||
static const esp_partition_t* update_partition = esp_ota_get_next_update_partition(NULL);
|
||||
static uint16_t packet_number = 0; // represents last written packet
|
||||
|
||||
class RemoteManagement {
|
||||
public:
|
||||
static void mark_ota_success();
|
||||
static bool start_ota();
|
||||
static bool write_ota(const uint8_t* ota_data, size_t size);
|
||||
static bool ota_end();
|
||||
static void restart();
|
||||
|
||||
static void register_calls(MessagingInterface &messaging_interface);
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
#endif // REMOTEMANAGEMENT_H
|
||||
149
main/include/util/ring_buffer.h
Normal file
149
main/include/util/ring_buffer.h
Normal file
@@ -0,0 +1,149 @@
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
template <typename T>
|
||||
class RingBuffer {
|
||||
public:
|
||||
explicit RingBuffer(size_t capacity)
|
||||
: m_buffer(capacity), m_capacity(capacity) {
|
||||
}
|
||||
|
||||
void push(const T& item) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
m_buffer[m_head] = item;
|
||||
|
||||
if (m_full) {
|
||||
m_tail = (m_tail + 1) % m_capacity;
|
||||
}
|
||||
|
||||
m_head = (m_head + 1) % m_capacity;
|
||||
m_full = m_head == m_tail;
|
||||
}
|
||||
|
||||
void push(T&& item) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
m_buffer[m_head] = std::move(item);
|
||||
|
||||
if (m_full) {
|
||||
m_tail = (m_tail + 1) % m_capacity;
|
||||
}
|
||||
|
||||
m_head = (m_head + 1) % m_capacity;
|
||||
m_full = m_head == m_tail;
|
||||
}
|
||||
|
||||
std::optional<T> pop() {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
if (empty_locked()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
T value = std::move(m_buffer[m_tail]);
|
||||
m_full = false;
|
||||
m_tail = (m_tail + 1) % m_capacity;
|
||||
return value;
|
||||
}
|
||||
|
||||
std::optional<T> peek() const {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (empty_locked()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return m_buffer[m_tail];
|
||||
}
|
||||
|
||||
std::vector<T> drain() {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
std::vector<T> out;
|
||||
size_t count = size_locked();
|
||||
out.reserve(count);
|
||||
|
||||
if (count == 0) {
|
||||
return out;
|
||||
}
|
||||
|
||||
size_t idx = m_tail;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
out.push_back(std::move(m_buffer[idx]));
|
||||
idx = (idx + 1) % m_capacity;
|
||||
}
|
||||
|
||||
m_head = 0;
|
||||
m_tail = 0;
|
||||
m_full = false;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<T> peek_drain() {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
std::vector<T> out;
|
||||
size_t count = size_locked();
|
||||
out.reserve(count);
|
||||
|
||||
if (count == 0) {
|
||||
return out;
|
||||
}
|
||||
|
||||
size_t idx = m_tail;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
out.push_back(m_buffer[idx]);
|
||||
idx = (idx + 1) % m_capacity;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_head = m_tail;
|
||||
m_full = false;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return empty_locked();
|
||||
}
|
||||
|
||||
bool full() const {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return m_full;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return size_locked();
|
||||
}
|
||||
|
||||
size_t capacity() const {
|
||||
return m_capacity;
|
||||
}
|
||||
|
||||
private:
|
||||
bool empty_locked() const {
|
||||
return (!m_full && (m_head == m_tail));
|
||||
}
|
||||
|
||||
size_t size_locked() const {
|
||||
if (m_full) return m_capacity;
|
||||
if (m_head >= m_tail) return m_head - m_tail;
|
||||
return m_capacity + m_head - m_tail;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<T> m_buffer;
|
||||
const size_t m_capacity;
|
||||
|
||||
size_t m_head = 0;
|
||||
size_t m_tail = 0;
|
||||
bool m_full = false;
|
||||
|
||||
// mutable allows const functions to modify/lock the mutex
|
||||
mutable std::mutex m_mutex;
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "RemoteDebugging.h"
|
||||
#include "RemoteManagement.h"
|
||||
#if !defined(RMT_TEST) || (defined(RMT_TEST) && RMT_TEST == 0)
|
||||
// #include <cstdio>
|
||||
// #include <memory>
|
||||
@@ -15,11 +17,14 @@ extern "C" [[noreturn]] void app_main(void) {
|
||||
|
||||
auto& config_manager = ConfigManager::get_instance(); // NOLINT - here for easily adding temporary config
|
||||
|
||||
esp_log_set_vprintf(RemoteDebugging::custom_log_write);
|
||||
|
||||
const auto loop_manager = std::make_unique<LoopManager>();
|
||||
xTaskCreate(reinterpret_cast<TaskFunction_t>(LoopManager::metadata_tx_loop),
|
||||
"metadata_tx", 3096, loop_manager.get(), 3, nullptr);
|
||||
xTaskCreate(reinterpret_cast<TaskFunction_t>(LoopManager::sensor_loop),
|
||||
"sensor_tx", 3096, loop_manager.get(), 3, nullptr);
|
||||
|
||||
RemoteDebugging::register_calls(loop_manager->get_messaging_interface());
|
||||
RemoteManagement::register_calls(loop_manager->get_messaging_interface());
|
||||
RemoteManagement::mark_ota_success();
|
||||
|
||||
loop_manager->control_loop();
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user