Make USB/WEB debug configurable

This commit is contained in:
Daniel Öster 2025-08-30 00:22:43 +03:00
parent a1e5bc57d0
commit aa9a4d429e
14 changed files with 115 additions and 162 deletions

View file

@ -64,7 +64,7 @@ void setup() {
init_serial();
// We print this after setting up serial, such that is also printed to serial with DEBUG_VIA_USB set.
// We print this after setting up serial, so that is also printed if configured to do so
logging.printf("Battery emulator %s build " __DATE__ " " __TIME__ "\n", version_number);
init_events();
@ -352,9 +352,6 @@ void init_serial() {
// Init Serial monitor
Serial.begin(115200);
while (!Serial) {}
#ifdef DEBUG_VIA_USB
Serial.println("__ OK __");
#endif // DEBUG_VIA_USB
}
void check_interconnect_available() {

View file

@ -109,10 +109,6 @@
//#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting
//#define LOG_TO_SD //Enable this line to log diagnostic data to SD card (WARNING, raises CPU load, do not use for production)
//#define LOG_CAN_TO_SD //Enable this line to log incoming/outgoing CAN & CAN-FD messages to SD card (WARNING, raises CPU load, do not use for production)
//#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs (WARNING, raises CPU load, do not use for production)
//#define DEBUG_VIA_WEB //Enable this line to log diagnostic data while program runs, which can be viewed via webpage (WARNING, slightly raises CPU load, do not use for production)
//#define DEBUG_CAN_DATA //Enable this line to print incoming/outgoing CAN & CAN-FD messages to USB serial (WARNING, raises CPU load, do not use for production)
/* CAN options */
//#define CAN_ADDON //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 chip (Needed for some inverters / double battery)
#define CRYSTAL_FREQUENCY_MHZ 8 //CAN_ADDON option, what is your MCP2515 add-on boards crystal frequency?
@ -225,7 +221,7 @@ extern IPAddress gateway;
extern IPAddress subnet;
#endif
#if defined(DEBUG_VIA_USB) || defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD)
#if defined(DEBUG_VIA_USB) || defined(LOG_TO_SD)
#define DEBUG_LOG
#endif

View file

@ -2,6 +2,7 @@
#include <Arduino.h>
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/logging.h"
uint8_t reverse_bits(uint8_t byte) {
uint8_t reversed = 0;
@ -122,9 +123,7 @@ void BmwSbox::transmit_can(unsigned long currentMillis) {
SBOX_100.data.u8[0] = 0x86; // Precharge relay only
prechargeStartTime = currentMillis;
contactorStatus = NEGATIVE;
#ifdef DEBUG_VIA_USB
Serial.println("S-BOX Precharge relay engaged");
#endif
logging.println("S-BOX Precharge relay engaged");
break;
case NEGATIVE:
if (currentMillis - prechargeStartTime >= CONTACTOR_CONTROL_T1) {
@ -132,9 +131,7 @@ void BmwSbox::transmit_can(unsigned long currentMillis) {
negativeStartTime = currentMillis;
contactorStatus = POSITIVE;
datalayer.shunt.precharging = true;
#ifdef DEBUG_VIA_USB
Serial.println("S-BOX Negative relay engaged");
#endif
logging.println("S-BOX Negative relay engaged");
}
break;
case POSITIVE:
@ -145,18 +142,14 @@ void BmwSbox::transmit_can(unsigned long currentMillis) {
positiveStartTime = currentMillis;
contactorStatus = PRECHARGE_OFF;
datalayer.shunt.precharging = false;
#ifdef DEBUG_VIA_USB
Serial.println("S-BOX Positive relay engaged");
#endif
logging.println("S-BOX Positive relay engaged");
}
break;
case PRECHARGE_OFF:
if (currentMillis - positiveStartTime >= CONTACTOR_CONTROL_T3) {
SBOX_100.data.u8[0] = 0x6A; // Negative + Positive
contactorStatus = COMPLETED;
#ifdef DEBUG_VIA_USB
Serial.println("S-BOX Precharge relay released");
#endif
logging.println("S-BOX Precharge relay released");
datalayer.shunt.contactors_engaged = true;
}
break;

View file

@ -108,18 +108,16 @@ uint32_t decode_uint32be(uint8_t data[8], uint8_t offset) {
((uint32_t)data[offset + 3]);
}
#ifdef DEBUG_VIA_USB
void dump_buff(const char* msg, uint8_t* buff, uint8_t len) {
Serial.print("[DALY-BMS] ");
Serial.print(msg);
logging.print("[DALY-BMS] ");
logging.print(msg);
for (int i = 0; i < len; i++) {
Serial.print(buff[i] >> 4, HEX);
Serial.print(buff[i] & 0xf, HEX);
Serial.print(" ");
logging.print(buff[i] >> 4, HEX);
logging.print(buff[i] & 0xf, HEX);
logging.print(" ");
}
Serial.println();
logging.println();
}
#endif
void decode_packet(uint8_t command, uint8_t data[8]) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
@ -180,9 +178,7 @@ void DalyBms::transmit_rs485(unsigned long currentMillis) {
tx_buff[2] = nextCommand;
tx_buff[3] = 8;
tx_buff[12] = calculate_checksum(tx_buff);
#ifdef DEBUG_VIA_USB
dump_buff("transmitting: ", tx_buff, 13);
#endif
Serial2.write(tx_buff, 13);
nextCommand++;
if (nextCommand > 0x98)
@ -202,17 +198,12 @@ void DalyBms::receive() {
if (recv_len > 0 && recv_buff[0] != 0xA5 || recv_len > 1 && recv_buff[1] != 0x01 ||
recv_len > 2 && (recv_buff[2] < 0x90 || recv_buff[2] > 0x98) || recv_len > 3 && recv_buff[3] != 8 ||
recv_len > 12 && recv_buff[12] != calculate_checksum(recv_buff)) {
#ifdef DEBUG_VIA_USB
dump_buff("dropping partial rx: ", recv_buff, recv_len);
#endif
recv_len = 0;
}
if (recv_len > 12) {
#ifdef DEBUG_VIA_USB
dump_buff("decoding successfull rx: ", recv_buff, recv_len);
#endif
decode_packet(recv_buff[2], &recv_buff[4]);
recv_len = 0;
lastPacket = millis();

View file

@ -135,6 +135,9 @@ void init_stored_settings() {
remote_bms_reset = settings.getBool("REMBMSRESET", false);
use_canfd_as_can = settings.getBool("CANFDASCAN", false);
datalayer.system.info.usb_logging_active = settings.getBool("USBENABLED", false);
datalayer.system.info.web_logging_active = settings.getBool("WEBENABLED", false);
// WIFI AP is enabled by default unless disabled in the settings
wifiap_enabled = settings.getBool("WIFIAPENABLED", true);
passwordAP = settings.getString("APPASSWORD", "123456789").c_str();

View file

@ -241,6 +241,10 @@ struct DATALAYER_SYSTEM_INFO_TYPE {
size_t logged_can_messages_offset = 0;
/** bool, determines if CAN messages should be logged for webserver */
bool can_logging_active = false;
/** bool, determines if USB serial logging should occur */
bool usb_logging_active = false;
/** bool, determines if general logging should be active for webserver */
bool web_logging_active = false;
/** uint8_t, enumeration which CAN interface should be used for log playback */
uint8_t can_replay_interface = CAN_NATIVE;
/** bool, determines if CAN replay should loop or not */

View file

@ -3,19 +3,17 @@
#include "../../datalayer/datalayer.h"
#include "../sdcard/sdcard.h"
#if defined(LOG_CAN_TO_SD) || defined(LOG_TO_SD)
#if !defined(HW_LILYGO)
#error The SD card logging feature is only available on LilyGo hardware
#endif
#endif
#define MAX_LINE_LENGTH_PRINTF 128
#define MAX_LENGTH_TIME_STR 14
bool previous_message_was_newline = true;
void Logging::add_timestamp(size_t size) {
#ifdef DEBUG_LOG
// Check if any logging is enabled at runtime
if (!datalayer.system.info.web_logging_active && !datalayer.system.info.usb_logging_active) {
return;
}
char* message_string = datalayer.system.info.logged_can_messages;
int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer
size_t message_string_size = sizeof(datalayer.system.info.logged_can_messages);
@ -23,43 +21,44 @@ void Logging::add_timestamp(size_t size) {
char* timestr;
static char timestr_buffer[MAX_LENGTH_TIME_STR];
#ifdef DEBUG_VIA_WEB
if (!datalayer.system.info.can_logging_active) {
/* If web debug is active and can logging is inactive,
* we use the debug logging memory directly for writing the timestring */
if (offset + size + MAX_LENGTH_TIME_STR > message_string_size) {
offset = 0;
if (datalayer.system.info.web_logging_active) {
if (!datalayer.system.info.can_logging_active) {
/* If web debug is active and can logging is inactive,
* we use the debug logging memory directly for writing the timestring */
if (offset + size + MAX_LENGTH_TIME_STR > message_string_size) {
offset = 0;
}
timestr = datalayer.system.info.logged_can_messages + offset;
} else {
timestr = timestr_buffer;
}
timestr = datalayer.system.info.logged_can_messages + offset;
} else {
timestr = timestr_buffer;
}
#else
timestr = timestr_buffer;
#endif // DEBUG_VIA_WEB
offset += min(MAX_LENGTH_TIME_STR - 1,
snprintf(timestr, MAX_LENGTH_TIME_STR, "%8lu.%03lu ", currentTime / 1000, currentTime % 1000));
#ifdef DEBUG_VIA_WEB
if (!datalayer.system.info.can_logging_active) {
if (datalayer.system.info.web_logging_active && !datalayer.system.info.can_logging_active) {
datalayer.system.info.logged_can_messages_offset = offset; // Update offset in buffer
}
#endif // DEBUG_VIA_WEB
// LOG_TO_SD remains as compile-time option for now
#ifdef LOG_TO_SD
add_log_to_buffer((uint8_t*)timestr, MAX_LENGTH_TIME_STR);
#endif // LOG_TO_SD
#ifdef DEBUG_VIA_USB
Serial.write(timestr);
#endif // DEBUG_VIA_USB
#endif // DEBUG_LOG
if (datalayer.system.info.usb_logging_active) {
Serial.write(timestr);
}
}
size_t Logging::write(const uint8_t* buffer, size_t size) {
#ifdef DEBUG_LOG
// Check if any logging is enabled at runtime
if (!datalayer.system.info.web_logging_active && !datalayer.system.info.usb_logging_active) {
return 0;
}
if (previous_message_was_newline) {
add_timestamp(size);
}
@ -67,11 +66,12 @@ size_t Logging::write(const uint8_t* buffer, size_t size) {
#ifdef LOG_TO_SD
add_log_to_buffer(buffer, size);
#endif
#ifdef DEBUG_VIA_USB
Serial.write(buffer, size);
#endif
#ifdef DEBUG_VIA_WEB
if (!datalayer.system.info.can_logging_active) {
if (datalayer.system.info.usb_logging_active) {
Serial.write(buffer, size);
}
if (datalayer.system.info.web_logging_active && !datalayer.system.info.can_logging_active) {
char* message_string = datalayer.system.info.logged_can_messages;
int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer
size_t message_string_size = sizeof(datalayer.system.info.logged_can_messages);
@ -82,16 +82,17 @@ size_t Logging::write(const uint8_t* buffer, size_t size) {
memcpy(message_string + offset, buffer, size);
datalayer.system.info.logged_can_messages_offset = offset + size; // Update offset in buffer
}
#endif // DEBUG_VIA_WEB
previous_message_was_newline = buffer[size - 1] == '\n';
return size;
#endif // DEBUG_LOG
return 0;
}
void Logging::printf(const char* fmt, ...) {
#ifdef DEBUG_LOG
// Check if any logging is enabled at runtime
if (!datalayer.system.info.web_logging_active && !datalayer.system.info.usb_logging_active) {
return;
}
if (previous_message_was_newline) {
add_timestamp(MAX_LINE_LENGTH_PRINTF);
}
@ -101,21 +102,22 @@ void Logging::printf(const char* fmt, ...) {
int offset = datalayer.system.info.logged_can_messages_offset; // Keeps track of the current position in the buffer
static char buffer[MAX_LINE_LENGTH_PRINTF];
char* message_buffer;
#ifdef DEBUG_VIA_WEB
if (!datalayer.system.info.can_logging_active) {
/* If web debug is active and can logging is inactive,
* we use the debug logging memory directly for writing the output */
if (offset + MAX_LINE_LENGTH_PRINTF > message_string_size) {
// Not enough space, reset and start from the beginning
offset = 0;
if (datalayer.system.info.web_logging_active) {
if (!datalayer.system.info.can_logging_active) {
/* If web debug is active and can logging is inactive,
* we use the debug logging memory directly for writing the output */
if (offset + MAX_LINE_LENGTH_PRINTF > message_string_size) {
// Not enough space, reset and start from the beginning
offset = 0;
}
message_buffer = message_string + offset;
} else {
message_buffer = buffer;
}
message_buffer = message_string + offset;
} else {
message_buffer = buffer;
}
#else
message_buffer = buffer;
#endif // DEBUG_VIA_WEB
va_list(args);
va_start(args, fmt);
@ -126,18 +128,15 @@ void Logging::printf(const char* fmt, ...) {
add_log_to_buffer((uint8_t*)message_buffer, size);
#endif // LOG_TO_SD
#ifdef DEBUG_VIA_USB
Serial.write(message_buffer, size);
#endif // DEBUG_VIA_USB
if (datalayer.system.info.usb_logging_active) {
Serial.write(message_buffer, size);
}
#ifdef DEBUG_VIA_WEB
if (!datalayer.system.info.can_logging_active) {
if (datalayer.system.info.web_logging_active && !datalayer.system.info.can_logging_active) {
// Data was already added to buffer, just move offset
datalayer.system.info.logged_can_messages_offset =
offset + size; // Keeps track of the current position in the buffer
}
#endif // DEBUG_VIA_WEB
previous_message_was_newline = message_buffer[size - 1] == '\n';
#endif // DEBUG_LOG
}

View file

@ -4,6 +4,7 @@
#include <Print.h>
#include <inttypes.h>
#include "../../../USER_SETTINGS.h"
#include "../../datalayer/datalayer.h"
#include "types.h"
class Logging : public Print {
@ -13,18 +14,23 @@ class Logging : public Print {
virtual size_t write(const uint8_t* buffer, size_t size);
virtual size_t write(uint8_t) { return 0; }
void printf(const char* fmt, ...);
void log_bms_status(real_bms_status_enum bms_status);
Logging() {}
};
extern Logging logging;
#ifdef DEBUG_LOG
#define DEBUG_PRINTF(fmt, ...) logging.printf(fmt, ##__VA_ARGS__)
#define DEBUG_PRINTLN(str) logging.println(str)
#else
#define DEBUG_PRINTF(fmt, ...) ((void)0)
#define DEBUG_PRINTLN(str) ((void)0)
#endif
// Replace compile-time macros with runtime checks
#define DEBUG_PRINTF(fmt, ...) \
do { \
if (datalayer.system.info.web_logging_active || datalayer.system.info.usb_logging_active) { \
logging.printf(fmt, ##__VA_ARGS__); \
} \
} while (0)
#define DEBUG_PRINTLN(str) \
do { \
if (datalayer.system.info.web_logging_active || datalayer.system.info.usb_logging_active) { \
logging.println(str); \
} \
} while (0)
#endif // __LOGGING_H__

View file

@ -3,7 +3,6 @@
#include "../../datalayer/datalayer.h"
#include "index_html.h"
#if defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD)
char* strnchr(const char* s, int c, size_t n) {
// Like strchr, but only searches the first 'n' bytes of the string.
@ -54,9 +53,9 @@ String debug_logger_processor(void) {
".can-message { background-color: #404E57; margin-bottom: 5px; padding: 10px; border-radius: 5px; font-family: "
"monospace; }";
content += "</style>";
#ifdef DEBUG_VIA_WEB
content += "<button onclick='refreshPage()'>Refresh data</button> ";
#endif
if (datalayer.system.info.web_logging_active) {
content += "<button onclick='refreshPage()'>Refresh data</button> ";
}
content += "<button onclick='exportLog()'>Export to .txt</button> ";
#ifdef LOG_TO_SD
content += "<button onclick='deleteLog()'>Delete log file</button> ";
@ -107,4 +106,3 @@ String debug_logger_processor(void) {
content += index_html_footer;
return content;
}
#endif

View file

@ -254,6 +254,14 @@ String settings_processor(const String& var, BatteryEmulatorSettingsStore& setti
return settings.getBool("WIFIAPENABLED", wifiap_enabled) ? "checked" : "";
}
if (var == "USBENABLED") {
return settings.getBool("USBENABLED") ? "checked" : "";
}
if (var == "WEBENABLED") {
return settings.getBool("WEBENABLED") ? "checked" : "";
}
if (var == "MQTTENABLED") {
return settings.getBool("MQTTENABLED") ? "checked" : "";
}
@ -862,6 +870,12 @@ const char* getCANInterfaceName(CAN_Interface interface) {
<label>Custom hostname: </label>
<input type='text' name='HOSTNAME' value="%HOSTNAME%" />
<label>Enable USB serial: </label>
<input type='checkbox' name='USBENABLED' value='on' style='margin-left: 0;' %USBENABLED% />
<label>Enable WEB logging: </label>
<input type='checkbox' name='WEBENABLED' value='on' style='margin-left: 0;' %WEBENABLED% />
<label>Enable MQTT: </label>
<input type='checkbox' name='MQTTENABLED' value='on' style='margin-left: 0;' %MQTTENABLED% />

View file

@ -267,13 +267,13 @@ void init_webserver() {
}
});
#if defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD)
// Route for going to debug logging web page
server.on("/log", HTTP_GET, [](AsyncWebServerRequest* request) {
AsyncWebServerResponse* response = request->beginResponse(200, "text/html", debug_logger_processor());
request->send(response);
});
#endif // DEBUG_VIA_WEB
if (datalayer.system.info.web_logging_active) { //|| defined(LOG_TO_SD)
// Route for going to debug logging web page
server.on("/log", HTTP_GET, [](AsyncWebServerRequest* request) {
AsyncWebServerResponse* response = request->beginResponse(200, "text/html", debug_logger_processor());
request->send(response);
});
}
// Define the handler to stop can logging
server.on("/stop_can_logging", HTTP_GET, [](AsyncWebServerRequest* request) {
@ -413,8 +413,8 @@ void init_webserver() {
};
const char* boolSettingNames[] = {
"DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "REMBMSRESET",
"CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", "INVICNT",
"DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "REMBMSRESET", "USBENABLED",
"WEBENABLED", "CANFDASCAN", "WIFIAPENABLED", "MQTTENABLED", "HADISC", "MQTTTOPICS", "INVICNT",
};
// Handles the form POST from UI to save settings of the common image
@ -1388,9 +1388,9 @@ String processor(const String& var) {
content += "<button onclick='Advanced()'>More Battery Info</button> ";
content += "<button onclick='CANlog()'>CAN logger</button> ";
content += "<button onclick='CANreplay()'>CAN replay</button> ";
#if defined(DEBUG_VIA_WEB) || defined(LOG_TO_SD)
content += "<button onclick='Log()'>Log</button> ";
#endif // DEBUG_VIA_WEB
if (datalayer.system.info.web_logging_active) { //|| defined(LOG_TO_SD)
content += "<button onclick='Log()'>Log</button> ";
}
content += "<button onclick='Cellmon()'>Cellmonitor</button> ";
content += "<button onclick='Events()'>Events</button> ";
content += "<button onclick='askReboot()'>Reboot Emulator</button>";

View file

@ -14,16 +14,13 @@ void KostalInverterProtocol::float2frame(uint8_t* arr, float value, uint8_t fram
}
static void dbg_timestamp(void) {
#ifdef DEBUG_KOSTAL_RS485_DATA
logging.print("[");
logging.print(millis());
logging.print(" ms] ");
#endif
}
static void dbg_frame(uint8_t* frame, int len, const char* prefix) {
dbg_timestamp();
#ifdef DEBUG_KOSTAL_RS485_DATA
logging.print(prefix);
logging.print(": ");
for (uint8_t i = 0; i < len; i++) {
@ -34,26 +31,11 @@ static void dbg_frame(uint8_t* frame, int len, const char* prefix) {
logging.print(" ");
}
logging.println("");
#endif
#ifdef DEBUG_KOSTAL_RS485_DATA_USB
Serial.print(prefix);
Serial.print(": ");
for (uint8_t i = 0; i < len; i++) {
if (frame[i] < 0x10) {
Serial.print("0");
}
Serial.print(frame[i], HEX);
Serial.print(" ");
}
Serial.println("");
#endif
}
static void dbg_message(const char* msg) {
dbg_timestamp();
#ifdef DEBUG_KOSTAL_RS485_DATA
logging.println(msg);
#endif
}
/* https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing#Encoding_examples */

View file

@ -8,13 +8,6 @@
#define SELECTED_INVERTER_CLASS KostalInverterProtocol
#endif
//#define DEBUG_KOSTAL_RS485_DATA // Enable this line to get TX / RX printed out via logging
//#define DEBUG_KOSTAL_RS485_DATA_USB // Enable this line to get TX / RX printed out via USB
#if defined(DEBUG_KOSTAL_RS485_DATA) && !defined(DEBUG_LOG)
#error "enable LOG_TO_SD, DEBUG_VIA_USB or DEBUG_VIA_WEB in order to use DEBUG_KOSTAL_RS485_DATA"
#endif
class KostalInverterProtocol : public Rs485InverterProtocol {
public:
const char* name() override { return Name; }

View file

@ -107,34 +107,11 @@ void PylonLvInverter::map_can_frame_to_variable(CAN_frame rx_frame) {
}
}
#ifdef DEBUG_VIA_USB
void dump_frame(CAN_frame* frame) {
Serial.print("[PYLON-LV] sending CAN frame ");
Serial.print(frame->ID, HEX);
Serial.print(": ");
for (int i = 0; i < 8; i++) {
Serial.print(frame->data.u8[i] >> 4, HEX);
Serial.print(frame->data.u8[i] & 0xf, HEX);
Serial.print(" ");
}
Serial.println();
}
#endif
void PylonLvInverter::transmit_can(unsigned long currentMillis) {
if (currentMillis - previousMillis1000ms >= 1000) {
previousMillis1000ms = currentMillis;
#ifdef DEBUG_VIA_USB
dump_frame(&PYLON_351);
dump_frame(&PYLON_355);
dump_frame(&PYLON_356);
dump_frame(&PYLON_359);
dump_frame(&PYLON_35C);
dump_frame(&PYLON_35E);
#endif
transmit_can_frame(&PYLON_351);
transmit_can_frame(&PYLON_355);
transmit_can_frame(&PYLON_356);