improve status handling for twai, add n2k state to UI

This commit is contained in:
andreas 2023-08-25 19:35:38 +02:00
parent ec95007d9d
commit 9fd25ab6a8
6 changed files with 79 additions and 54 deletions

View File

@ -37,9 +37,12 @@ void GwLog::logString(const char *fmt,...){
xSemaphoreGive(locker); xSemaphoreGive(locker);
} }
void GwLog::logDebug(int level,const char *fmt,...){ void GwLog::logDebug(int level,const char *fmt,...){
if (level > logLevel) return;
va_list args; va_list args;
va_start(args,fmt); va_start(args,fmt);
logDebug(level,fmt,args);
}
void GwLog::logDebug(int level,const char *fmt,va_list args){
if (level > logLevel) return;
xSemaphoreTake(locker, portMAX_DELAY); xSemaphoreTake(locker, portMAX_DELAY);
recordCounter++; recordCounter++;
vsnprintf(buffer,bufferSize-1,fmt,args); vsnprintf(buffer,bufferSize-1,fmt,args);

View File

@ -27,6 +27,7 @@ class GwLog{
void setWriter(GwLogWriter *writer); void setWriter(GwLogWriter *writer);
void logString(const char *fmt,...); void logString(const char *fmt,...);
void logDebug(int level, const char *fmt,...); void logDebug(int level, const char *fmt,...);
void logDebug(int level, const char *fmt,va_list ap);
int isActive(int level){return level <= logLevel;}; int isActive(int level){return level <= logLevel;};
void flush(); void flush();
void setLevel(int level){this->logLevel=level;} void setLevel(int level){this->logLevel=level;}

View File

@ -3,22 +3,10 @@
#include "driver/twai.h" #include "driver/twai.h"
#include "GwLog.h" #include "GwLog.h"
#define TWAI_DEBUG 0 #define LOGID(id) ((id >> 8) & 0x1ffff)
#if TWAI_DEBUG == 1
#define TWAI_LOG(...) LOG_DEBUG(__VA_ARGS__)
#define TWAI_LDEBUG(...)
#else
#if TWAI_DEBUG == 2
#define TWAI_LOG(...) LOG_DEBUG(__VA_ARGS__)
#define TWA_LDEBUG(...) LOG_DEBUG(__VA_ARGS__)
#else
#define TWAI_LOG(...)
#define TWAI_LDEBUG(...)
#endif
#endif
Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin,GwLog *l): Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin):
tNMEA2000(),RxPin(_RxPin),TxPin(_TxPin),logger(l) tNMEA2000(),RxPin(_RxPin),TxPin(_TxPin)
{ {
} }
@ -32,21 +20,21 @@ bool Nmea2kTwai::CANSendFrame(unsigned long id, unsigned char len, const unsigne
memcpy(message.data,buf,len); memcpy(message.data,buf,len);
esp_err_t rt=twai_transmit(&message,0); esp_err_t rt=twai_transmit(&message,0);
if (rt != ESP_OK){ if (rt != ESP_OK){
TWAI_LOG(GwLog::DEBUG,"twai transmit for %ld failed: %x",(id & 0x1ffff),(int)rt); logDebug(LOG_DEBUG,"twai transmit for %ld failed: %x",LOGID(id),(int)rt);
return false; return false;
} }
TWAI_LDEBUG(GwLog::DEBUG,"twai transmit id %ld, len %d",(id & 0x1ffff),(int)len); logDebug(LOG_MSG,"twai transmit id %ld, len %d",LOGID(id),(int)len);
return true; return true;
} }
bool Nmea2kTwai::CANOpen() bool Nmea2kTwai::CANOpen()
{ {
esp_err_t rt=twai_start(); esp_err_t rt=twai_start();
if (rt != ESP_OK){ if (rt != ESP_OK){
LOG_DEBUG(GwLog::ERROR,"CANOpen failed: %x",(int)rt); logDebug(LOG_ERR,"CANOpen failed: %x",(int)rt);
return false; return false;
} }
else{ else{
LOG_DEBUG(GwLog::LOG,"CANOpen ok"); logDebug(LOG_INFO,"CANOpen ok");
} }
return true; return true;
} }
@ -63,10 +51,10 @@ bool Nmea2kTwai::CANGetFrame(unsigned long &id, unsigned char &len, unsigned cha
id=message.identifier; id=message.identifier;
len=message.data_length_code; len=message.data_length_code;
if (len > 8){ if (len > 8){
TWAI_LOG(GwLog::ERROR,"twai: received invalid message %lld, len %d",id,len); logDebug(LOG_DEBUG,"twai: received invalid message %lld, len %d",LOGID(id),len);
len=8; len=8;
} }
TWAI_LDEBUG(GwLog::DEBUG,"twai rcv id=%ld,len=%d, ext=%d",message.identifier,message.data_length_code,message.extd); logDebug(LOG_MSG,"twai rcv id=%ld,len=%d, ext=%d",LOGID(message.identifier),message.data_length_code,message.extd);
if (! message.rtr){ if (! message.rtr){
memcpy(buf,message.data,message.data_length_code); memcpy(buf,message.data,message.data_length_code);
} }
@ -79,10 +67,10 @@ void Nmea2kTwai::initDriver(){
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
esp_err_t rt=twai_driver_install(&g_config, &t_config, &f_config); esp_err_t rt=twai_driver_install(&g_config, &t_config, &f_config);
if (rt == ESP_OK) { if (rt == ESP_OK) {
LOG_DEBUG(GwLog::LOG,"twai driver initialzed, rx=%d,tx=%d",(int)RxPin,(int)TxPin); logDebug(LOG_INFO,"twai driver initialzed, rx=%d,tx=%d",(int)RxPin,(int)TxPin);
} }
else{ else{
LOG_DEBUG(GwLog::ERROR,"twai driver init failed: %x",(int)rt); logDebug(LOG_ERR,"twai driver init failed: %x",(int)rt);
} }
} }
// This will be called on Open() before any other initialization. Inherit this, if buffers can be set for the driver // This will be called on Open() before any other initialization. Inherit this, if buffers can be set for the driver
@ -93,38 +81,38 @@ void Nmea2kTwai::InitCANFrameBuffers()
tNMEA2000::InitCANFrameBuffers(); tNMEA2000::InitCANFrameBuffers();
} }
Nmea2kTwai::STATE Nmea2kTwai::getState(){ Nmea2kTwai::Status Nmea2kTwai::getStatus(){
twai_status_info_t state; twai_status_info_t state;
Status rt;
if (twai_get_status_info(&state) != ESP_OK){ if (twai_get_status_info(&state) != ESP_OK){
return ST_ERROR; return rt;
} }
switch(state.state){ switch(state.state){
case TWAI_STATE_STOPPED: case TWAI_STATE_STOPPED:
return ST_STOPPED; rt.state=ST_STOPPED;
break;
case TWAI_STATE_RUNNING: case TWAI_STATE_RUNNING:
return ST_RUNNING; rt.state=ST_RUNNING;
break;
case TWAI_STATE_BUS_OFF: case TWAI_STATE_BUS_OFF:
return ST_BUS_OFF; rt.state=ST_BUS_OFF;
break;
case TWAI_STATE_RECOVERING: case TWAI_STATE_RECOVERING:
return ST_RECOVERING; rt.state=ST_RECOVERING;
} break;
return ST_ERROR;
}
Nmea2kTwai::ERRORS Nmea2kTwai::getErrors(){
ERRORS rt;
twai_status_info_t state;
if (twai_get_status_info(&state) != ESP_OK){
return rt;
} }
rt.rx_errors=state.rx_error_counter; rt.rx_errors=state.rx_error_counter;
rt.tx_errors=state.tx_error_counter; rt.tx_errors=state.tx_error_counter;
rt.tx_failed=state.tx_failed_count; rt.tx_failed=state.tx_failed_count;
rt.rx_missed=state.rx_missed_count;
rt.rx_overrun=state.rx_overrun_count;
return rt; return rt;
} }
bool Nmea2kTwai::startRecovery(){ bool Nmea2kTwai::startRecovery(){
esp_err_t rt=twai_driver_uninstall(); esp_err_t rt=twai_driver_uninstall();
if (rt != ESP_OK){ if (rt != ESP_OK){
LOG_DEBUG(GwLog::ERROR,"twai: deinit for recovery failed with %x",(int)rt); logDebug(LOG_ERR,"twai: deinit for recovery failed with %x",(int)rt);
} }
initDriver(); initDriver();
bool frt=CANOpen(); bool frt=CANOpen();

View File

@ -5,7 +5,7 @@
class Nmea2kTwai : public tNMEA2000{ class Nmea2kTwai : public tNMEA2000{
public: public:
Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin,GwLog *logger); Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin);
typedef enum{ typedef enum{
ST_STOPPED, ST_STOPPED,
ST_RUNNING, ST_RUNNING,
@ -14,15 +14,23 @@ class Nmea2kTwai : public tNMEA2000{
ST_ERROR ST_ERROR
} STATE; } STATE;
typedef struct{ typedef struct{
//see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/twai.html#_CPPv418twai_status_info_t
uint32_t rx_errors=0; uint32_t rx_errors=0;
uint32_t tx_errors=0; uint32_t tx_errors=0;
uint32_t tx_failed=0; uint32_t tx_failed=0;
} ERRORS; uint32_t rx_missed=0;
STATE getState(); uint32_t rx_overrun=0;
ERRORS getErrors(); STATE state=ST_ERROR;
} Status;
Status getStatus();
bool startRecovery(); bool startRecovery();
static const char * stateStr(const STATE &st); static const char * stateStr(const STATE &st);
virtual bool CANOpen(); virtual bool CANOpen();
virtual ~Nmea2kTwai(){};
static const int LOG_ERR=0;
static const int LOG_INFO=1;
static const int LOG_DEBUG=2;
static const int LOG_MSG=3;
protected: protected:
// Virtual functions for different interfaces. Currently there are own classes // Virtual functions for different interfaces. Currently there are own classes
// for Arduino due internal CAN (NMEA2000_due), external MCP2515 SPI CAN bus controller (NMEA2000_mcp), // for Arduino due internal CAN (NMEA2000_due), external MCP2515 SPI CAN bus controller (NMEA2000_mcp),
@ -32,14 +40,13 @@ class Nmea2kTwai : public tNMEA2000{
// This will be called on Open() before any other initialization. Inherit this, if buffers can be set for the driver // This will be called on Open() before any other initialization. Inherit this, if buffers can be set for the driver
// and you want to change size of library send frame buffer size. See e.g. NMEA2000_teensy.cpp. // and you want to change size of library send frame buffer size. See e.g. NMEA2000_teensy.cpp.
virtual void InitCANFrameBuffers(); virtual void InitCANFrameBuffers();
virtual void logDebug(int level,const char *fmt,...){}
private: private:
void initDriver(); void initDriver();
gpio_num_t TxPin; gpio_num_t TxPin;
gpio_num_t RxPin; gpio_num_t RxPin;
GwLog *logger;
}; };
#endif #endif

View File

@ -110,7 +110,22 @@ GwLog logger(LOGLEVEL,NULL);
GwConfigHandler config(&logger); GwConfigHandler config(&logger);
#include "Nmea2kTwai.h" #include "Nmea2kTwai.h"
Nmea2kTwai &NMEA2000=*(new Nmea2kTwai(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,&logger)); class Nmea2kTwaiLog : public Nmea2kTwai{
private:
GwLog* logger;
public:
Nmea2kTwaiLog(gpio_num_t _TxPin, gpio_num_t _RxPin, GwLog *l):
Nmea2kTwai(_TxPin,_RxPin),logger(l){}
virtual void logDebug(int level, const char *fmt,...){
va_list args;
va_start(args,fmt);
if (level > 2) level++; //error+info+debug are similar, map msg to 4
logger->logDebug(level,fmt,args);
}
};
Nmea2kTwai &NMEA2000=*(new Nmea2kTwaiLog(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,&logger));
#ifdef GWBUTTON_PIN #ifdef GWBUTTON_PIN
bool fixedApPass=false; bool fixedApPass=false;
@ -354,7 +369,7 @@ public:
protected: protected:
virtual void processRequest() virtual void processRequest()
{ {
GwJsonDocument status(256 + GwJsonDocument status(290 +
countNMEA2KIn.getJsonSize()+ countNMEA2KIn.getJsonSize()+
countNMEA2KOut.getJsonSize() + countNMEA2KOut.getJsonSize() +
channels.getJsonSize() channels.getJsonSize()
@ -370,6 +385,8 @@ protected:
status["salt"] = buffer; status["salt"] = buffer;
status["fwtype"]= firmwareType; status["fwtype"]= firmwareType;
status["heap"]=(long)xPortGetFreeHeapSize(); status["heap"]=(long)xPortGetFreeHeapSize();
Nmea2kTwai::Status n2kState=NMEA2000.getStatus();
status["n2kstate"]=NMEA2000.stateStr(n2kState.state);
//nmea0183Converter->toJson(status); //nmea0183Converter->toJson(status);
countNMEA2KIn.toJson(status); countNMEA2KIn.toJson(status);
countNMEA2KOut.toJson(status); countNMEA2KOut.toJson(status);
@ -843,15 +860,20 @@ void loop() {
} }
if (now > (lastCanRecovery + CAN_RECOVERY_PERIOD)){ if (now > (lastCanRecovery + CAN_RECOVERY_PERIOD)){
lastCanRecovery=now; lastCanRecovery=now;
Nmea2kTwai::STATE canState=NMEA2000.getState(); Nmea2kTwai::Status canState=NMEA2000.getStatus();
Nmea2kTwai::ERRORS canErrors=NMEA2000.getErrors(); logger.logDebug(GwLog::DEBUG,"can state %s, rxerr %d, txerr %d, txfail %d, rxmiss %d, rxoverrun %d",
logger.logDebug(GwLog::DEBUG,"can state %s, rxerr %d, txerr %d, txfail %d",NMEA2000.stateStr(canState),canErrors.rx_errors,canErrors.tx_errors,canErrors.tx_failed); NMEA2000.stateStr(canState.state),
if (canState != Nmea2kTwai::ST_RUNNING){ canState.rx_errors,
if (canState == Nmea2kTwai::ST_BUS_OFF){ canState.tx_errors,
canState.tx_failed,
canState.rx_missed,
canState.rx_overrun);
if (canState.state != Nmea2kTwai::ST_RUNNING){
if (canState.state == Nmea2kTwai::ST_BUS_OFF){
bool rt=NMEA2000.startRecovery(); bool rt=NMEA2000.startRecovery();
logger.logDebug(GwLog::LOG,"start can recovery - result %d",(int)rt); logger.logDebug(GwLog::LOG,"start can recovery - result %d",(int)rt);
} }
if (canState == Nmea2kTwai::ST_STOPPED){ if (canState.state == Nmea2kTwai::ST_STOPPED){
bool rt=NMEA2000.CANOpen(); bool rt=NMEA2000.CANOpen();
logger.logDebug(GwLog::LOG,"restart can driver - result %d",(int)rt); logger.logDebug(GwLog::LOG,"restart can driver - result %d",(int)rt);
} }

View File

@ -58,7 +58,11 @@
<div class="row"> <div class="row">
<span class="label">Free heap</span> <span class="label">Free heap</span>
<span class="value" id="heap">---</span> <span class="value" id="heap">---</span>
</div> </div>
<div class="row">
<span class="label">N2K State</span>
<span class="value" id="n2kstate">UNKNOWN</span>
</div>
</div> </div>
<button id="reset">Reset</button> <button id="reset">Reset</button>
</div> </div>