allow to configure sendRMC

This commit is contained in:
andreas 2024-03-03 18:37:38 +01:00
parent e10432dae4
commit 47a2eb52f5
7 changed files with 206 additions and 145 deletions

View File

@ -0,0 +1,41 @@
/*
(C) Andreas Vogel andreas@wellenvogel.de
This code is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _GWCONVERTERCONFIG_H
#define _GWCONVERTERCONFIG_H
#include "GWConfig.h"
class GwConverterConfig{
public:
int minXdrInterval=100;
int starboardRudderInstance=0;
int portRudderInstance=-1; //ignore
int min2KInterval=50;
int rmcInterval=1000;
int rmcCheckTime=4000;
void init(GwConfigHandler *config){
minXdrInterval=config->getInt(GwConfigDefinitions::minXdrInterval,100);
starboardRudderInstance=config->getInt(GwConfigDefinitions::stbRudderI,0);
portRudderInstance=config->getInt(GwConfigDefinitions::portRudderI,-1);
min2KInterval=config->getInt(GwConfigDefinitions::min2KInterval,50);
if (min2KInterval < 10)min2KInterval=10;
rmcCheckTime=config->getInt(GwConfigDefinitions::checkRMCt,4000);
if (rmcCheckTime < 1000) rmcCheckTime=1000;
rmcInterval=config->getInt(GwConfigDefinitions::sendRMCi,1000);
if (rmcInterval < 0) rmcInterval=0;
if (rmcInterval > 0 && rmcInterval <100) rmcInterval=100;
}
};
#endif

View File

@ -29,7 +29,6 @@ private:
MyAisDecoder *aisDecoder=NULL; MyAisDecoder *aisDecoder=NULL;
ConverterList<NMEA0183DataToN2KFunctions, SNMEA0183Msg> converters; ConverterList<NMEA0183DataToN2KFunctions, SNMEA0183Msg> converters;
std::map<String,unsigned long> lastSends; std::map<String,unsigned long> lastSends;
unsigned long minSendInterval=50;
GwXDRMappings *xdrMappings; GwXDRMappings *xdrMappings;
class WaypointNumber{ class WaypointNumber{
public: public:
@ -92,7 +91,7 @@ private:
return false; return false;
} }
bool send(tN2kMsg &msg, int sourceId,String key=""){ bool send(tN2kMsg &msg, int sourceId,String key=""){
return send(msg,key,minSendInterval,sourceId); return send(msg,key,config.min2KInterval,sourceId);
} }
bool updateDouble(GwBoatItem<double> *target,double v, int sourceId){ bool updateDouble(GwBoatItem<double> *target,double v, int sourceId){
if (v != NMEA0183DoubleNA){ if (v != NMEA0183DoubleNA){
@ -304,7 +303,7 @@ private:
LOG_DEBUG(GwLog::DEBUG + 1, "convert RMB"); LOG_DEBUG(GwLog::DEBUG + 1, "convert RMB");
tRMB rmb; tRMB rmb;
if (! NMEA0183ParseRMB_nc(msg,rmb)){ if (! NMEA0183ParseRMB_nc(msg,rmb)){
LOG_DEBUG(GwLog::DEBUG, "failed to parse RMC %s", msg.line); LOG_DEBUG(GwLog::DEBUG, "failed to parse RMB %s", msg.line);
return; return;
} }
tN2kMsg n2kMsg; tN2kMsg n2kMsg;
@ -359,6 +358,7 @@ private:
LOG_DEBUG(GwLog::DEBUG, "invalid status %c for RMC %s",status, msg.line); LOG_DEBUG(GwLog::DEBUG, "invalid status %c for RMC %s",status, msg.line);
return; return;
} }
lastRmc=millis(); //we received an RMC that is not from us
tN2kMsg n2kMsg; tN2kMsg n2kMsg;
if ( if (
UD(GPST) && UD(GPST) &&
@ -666,21 +666,33 @@ private:
void convertDBT(const SNMEA0183Msg &msg){ void convertDBT(const SNMEA0183Msg &msg){
return convertDBKx(msg,DBT); return convertDBKx(msg,DBT);
} }
#define validInstance(name) (name >= 0 && name <= 253)
void convertRSA(const SNMEA0183Msg &msg){ void convertRSA(const SNMEA0183Msg &msg){
double RPOS=NMEA0183DoubleNA; double RPOS=NMEA0183DoubleNA;
double PRPOS=NMEA0183DoubleNA;
if (msg.FieldCount() < 4) if (msg.FieldCount() < 4)
{ {
LOG_DEBUG(GwLog::DEBUG, "failed to parse RSA %s", msg.line); LOG_DEBUG(GwLog::DEBUG, "failed to parse RSA %s", msg.line);
return; return;
} }
if (msg.FieldLen(0)>0){
if (msg.Field(1)[0] != 'A') return;
RPOS=degToRad*atof(msg.Field(0));
tN2kMsg n2kMsg; tN2kMsg n2kMsg;
if (! UD(RPOS)) return; if (msg.FieldLen(0)>0){
SetN2kRudder(n2kMsg,RPOS); if (msg.Field(1)[0] == 'A'){
send(n2kMsg,msg.sourceId); RPOS=degToRad*atof(msg.Field(0));
if (UD(RPOS) && validInstance(config.starboardRudderInstance)) {
SetN2kRudder(n2kMsg,RPOS,config.starboardRudderInstance);
send(n2kMsg,msg.sourceId,"127245S");
}
}
}
if (msg.FieldLen(2)>0){
if (msg.Field(3)[0] == 'A'){
PRPOS=degToRad*atof(msg.Field(2));
if (UD(PRPOS) && validInstance(config.portRudderInstance)){
SetN2kRudder(n2kMsg,PRPOS,config.portRudderInstance);
send(n2kMsg,msg.sourceId,"127245P");
}
}
} }
} }
@ -1061,10 +1073,10 @@ public:
NMEA0183DataToN2KFunctions(GwLog *logger, GwBoatData *boatData, N2kSender callback, NMEA0183DataToN2KFunctions(GwLog *logger, GwBoatData *boatData, N2kSender callback,
GwXDRMappings *xdrMappings, GwXDRMappings *xdrMappings,
unsigned long minSendInterval) const GwConverterConfig &cfg)
: NMEA0183DataToN2K(logger, boatData, callback) : NMEA0183DataToN2K(logger, boatData, callback)
{ {
this->minSendInterval=minSendInterval; this->config=cfg;
this->xdrMappings=xdrMappings; this->xdrMappings=xdrMappings;
aisDecoder= new MyAisDecoder(logger,this->sender); aisDecoder= new MyAisDecoder(logger,this->sender);
registerConverters(); registerConverters();
@ -1074,7 +1086,7 @@ public:
NMEA0183DataToN2K* NMEA0183DataToN2K::create(GwLog *logger,GwBoatData *boatData,N2kSender callback, NMEA0183DataToN2K* NMEA0183DataToN2K::create(GwLog *logger,GwBoatData *boatData,N2kSender callback,
GwXDRMappings *xdrMappings, GwXDRMappings *xdrMappings,
unsigned long minSendInterval){ const GwConverterConfig &config){
return new NMEA0183DataToN2KFunctions(logger, boatData,callback,xdrMappings,minSendInterval); return new NMEA0183DataToN2KFunctions(logger, boatData,callback,xdrMappings,config);
} }

View File

@ -4,6 +4,7 @@
#include "GwBoatData.h" #include "GwBoatData.h"
#include "N2kMessages.h" #include "N2kMessages.h"
#include "GwXDRMappings.h" #include "GwXDRMappings.h"
#include "GwConverterConfig.h"
class NMEA0183DataToN2K{ class NMEA0183DataToN2K{
public: public:
@ -12,14 +13,17 @@ class NMEA0183DataToN2K{
GwLog * logger; GwLog * logger;
GwBoatData *boatData; GwBoatData *boatData;
N2kSender sender; N2kSender sender;
GwConverterConfig config;
unsigned long lastRmc=millis();
public: public:
NMEA0183DataToN2K(GwLog *logger,GwBoatData *boatData,N2kSender callback); NMEA0183DataToN2K(GwLog *logger,GwBoatData *boatData,N2kSender callback);
virtual bool parseAndSend(const char *buffer, int sourceId)=0; virtual bool parseAndSend(const char *buffer, int sourceId)=0;
virtual unsigned long *handledPgns()=0; virtual unsigned long *handledPgns()=0;
virtual int numConverters()=0; virtual int numConverters()=0;
virtual String handledKeys()=0; virtual String handledKeys()=0;
unsigned long getLastRmc()const {return lastRmc; }
static NMEA0183DataToN2K* create(GwLog *logger,GwBoatData *boatData,N2kSender callback, static NMEA0183DataToN2K* create(GwLog *logger,GwBoatData *boatData,N2kSender callback,
GwXDRMappings *xdrMappings, GwXDRMappings *xdrMappings,
unsigned long minSendInterval); const GwConverterConfig &config);
}; };
#endif #endif

View File

@ -43,7 +43,7 @@ N2kDataToNMEA0183::N2kDataToNMEA0183(GwLog * logger, GwBoatData *boatData,
//***************************************************************************** //*****************************************************************************
void N2kDataToNMEA0183::loop() { void N2kDataToNMEA0183::loop(unsigned long) {
} }
//***************************************************************************** //*****************************************************************************
@ -65,10 +65,10 @@ private:
GwXDRMappings *xdrMappings; GwXDRMappings *xdrMappings;
ConverterList<N2kToNMEA0183Functions,tN2kMsg> converters; ConverterList<N2kToNMEA0183Functions,tN2kMsg> converters;
std::map<String,unsigned long> lastSendTransducers; std::map<String,unsigned long> lastSendTransducers;
static const unsigned long RMCPeriod = 500;
tNMEA0183Msg xdrMessage; tNMEA0183Msg xdrMessage;
bool xdrOpened=false; bool xdrOpened=false;
int xdrCount=0; int xdrCount=0;
unsigned long lastRmcSent=0;
bool addToXdr(GwXDRFoundMapping::XdrEntry entry){ bool addToXdr(GwXDRFoundMapping::XdrEntry entry){
auto it=lastSendTransducers.find(entry.transducer); auto it=lastSendTransducers.find(entry.transducer);
@ -133,9 +133,6 @@ private:
return boatData->update((double)value,sourceId,mapping); return boatData->update((double)value,sourceId,mapping);
} }
unsigned long LastPosSend;
unsigned long NextRMCSend;
unsigned long lastLoopTime;
virtual unsigned long *handledPgns() virtual unsigned long *handledPgns()
{ {
@ -165,7 +162,6 @@ private:
{ {
return converters.numConverters(); return converters.numConverters();
} }
void SetNextRMCSend() { NextRMCSend = millis() + RMCPeriod; }
//*************** the converters *********************** //*************** the converters ***********************
void HandleHeading(const tN2kMsg &N2kMsg) void HandleHeading(const tN2kMsg &N2kMsg)
@ -545,11 +541,9 @@ private:
void SendRMC() void SendRMC()
{ {
long now = millis(); long now = millis();
if (NextRMCSend <= millis() && if (boatData->LAT->isValid(now) && boatData->LON->isValid(now))
boatData->LAT->isValid(now) &&
boatData->LAT->getLastSource() == sourceId
)
{ {
lastRmcSent=now;
tNMEA0183Msg NMEA0183Msg; tNMEA0183Msg NMEA0183Msg;
if (NMEA0183SetRMC(NMEA0183Msg, if (NMEA0183SetRMC(NMEA0183Msg,
@ -564,7 +558,6 @@ private:
{ {
SendMessage(NMEA0183Msg); SendMessage(NMEA0183Msg);
} }
SetNextRMCSend();
} }
} }
@ -610,10 +603,8 @@ private:
if (ParseN2kRudder(N2kMsg, RudderPosition, Instance, RudderDirectionOrder, AngleOrder)) if (ParseN2kRudder(N2kMsg, RudderPosition, Instance, RudderDirectionOrder, AngleOrder))
{ {
bool main=false;
if (Instance == config.starboardRudderInstance){ if (Instance == config.starboardRudderInstance){
updateDouble(boatData->RPOS, RudderPosition); updateDouble(boatData->RPOS, RudderPosition);
main=true;
} }
else if (Instance == config.portRudderInstance){ else if (Instance == config.portRudderInstance){
updateDouble(boatData->PRPOS, RudderPosition); updateDouble(boatData->PRPOS, RudderPosition);
@ -626,18 +617,24 @@ private:
if (!NMEA0183Msg.Init("RSA", talkerId)) if (!NMEA0183Msg.Init("RSA", talkerId))
return; return;
if (main){ auto rpos=boatData->RPOS;
if (!NMEA0183Msg.AddDoubleField(formatWind(RudderPosition)))return; if (rpos->isValid()){
if (!NMEA0183Msg.AddDoubleField(formatWind(rpos->getData())))return;
if (!NMEA0183Msg.AddStrField("A"))return; if (!NMEA0183Msg.AddStrField("A"))return;
if (!NMEA0183Msg.AddDoubleField(0.0))return;
if (!NMEA0183Msg.AddStrField("V"))return;
} }
else{ else{
if (!NMEA0183Msg.AddDoubleField(0.0))return; if (!NMEA0183Msg.AddDoubleField(0.0))return;
if (!NMEA0183Msg.AddStrField("V"))return; if (!NMEA0183Msg.AddStrField("V"))return;
if (!NMEA0183Msg.AddDoubleField(formatWind(RudderPosition)))return; }
auto prpos=boatData->PRPOS;
if (prpos->isValid()){
if (!NMEA0183Msg.AddDoubleField(formatWind(prpos->getData())))return;
if (!NMEA0183Msg.AddStrField("A"))return; if (!NMEA0183Msg.AddStrField("A"))return;
} }
else{
if (!NMEA0183Msg.AddDoubleField(0.0))return;
if (!NMEA0183Msg.AddStrField("V"))return;
}
SendMessage(NMEA0183Msg); SendMessage(NMEA0183Msg);
} }
} }
@ -1517,34 +1514,29 @@ private:
public: public:
N2kToNMEA0183Functions(GwLog *logger, GwBoatData *boatData, N2kToNMEA0183Functions(GwLog *logger, GwBoatData *boatData,
SendNMEA0183MessageCallback callback, SendNMEA0183MessageCallback callback,
String talkerId, GwXDRMappings *xdrMappings, const Config &cfg) String talkerId, GwXDRMappings *xdrMappings, const GwConverterConfig &cfg)
: N2kDataToNMEA0183(logger, boatData, callback,talkerId) : N2kDataToNMEA0183(logger, boatData, callback,talkerId)
{ {
LastPosSend = 0;
lastLoopTime = 0;
NextRMCSend = millis() + RMCPeriod;
this->logger = logger; this->logger = logger;
this->boatData = boatData; this->boatData = boatData;
this->xdrMappings=xdrMappings; this->xdrMappings=xdrMappings;
this->config=cfg; this->config=cfg;
registerConverters(); registerConverters();
} }
virtual void loop() virtual void loop(unsigned long lastExtRmc) override
{ {
N2kDataToNMEA0183::loop(); N2kDataToNMEA0183::loop(lastExtRmc);
unsigned long now = millis(); unsigned long now = millis();
if (now < (lastLoopTime + 100)) if (config.rmcInterval > 0 && (lastExtRmc + config.rmcCheckTime) <= now && (lastRmcSent + config.rmcInterval) <= now){
return;
lastLoopTime = now;
SendRMC(); SendRMC();
} }
}
}; };
N2kDataToNMEA0183* N2kDataToNMEA0183::create(GwLog *logger, GwBoatData *boatData, N2kDataToNMEA0183* N2kDataToNMEA0183::create(GwLog *logger, GwBoatData *boatData,
SendNMEA0183MessageCallback callback, String talkerId, GwXDRMappings *xdrMappings, SendNMEA0183MessageCallback callback, String talkerId, GwXDRMappings *xdrMappings,
const N2kDataToNMEA0183::Config &cfg){ const GwConverterConfig &cfg){
LOG_DEBUG(GwLog::LOG,"creating N2kToNMEA0183"); LOG_DEBUG(GwLog::LOG,"creating N2kToNMEA0183");
return new N2kToNMEA0183Functions(logger,boatData,callback, talkerId,xdrMappings,cfg); return new N2kToNMEA0183Functions(logger,boatData,callback, talkerId,xdrMappings,cfg);
} }

View File

@ -26,9 +26,10 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <NMEA0183.h> #include <NMEA0183.h>
#include <NMEA2000.h> #include <NMEA2000.h>
#include <GwLog.h> #include "GwLog.h"
#include <GwBoatData.h> #include "GwBoatData.h"
#include <GwXDRMappings.h> #include "GwXDRMappings.h"
#include "GwConverterConfig.h"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
class GwJsonDocument; class GwJsonDocument;
@ -36,14 +37,8 @@ class N2kDataToNMEA0183
{ {
public: public:
typedef std::function<void(const tNMEA0183Msg &NMEA0183Msg,int id)> SendNMEA0183MessageCallback; typedef std::function<void(const tNMEA0183Msg &NMEA0183Msg,int id)> SendNMEA0183MessageCallback;
class Config{
public:
int minXdrInterval=100;
int starboardRudderInstance=0;
int portRudderInstance=-1; //ignore
};
protected: protected:
Config config; GwConverterConfig config;
GwLog *logger; GwLog *logger;
GwBoatData *boatData; GwBoatData *boatData;
int sourceId=0; int sourceId=0;
@ -55,9 +50,9 @@ protected:
public: public:
static N2kDataToNMEA0183* create(GwLog *logger, GwBoatData *boatData, SendNMEA0183MessageCallback callback, static N2kDataToNMEA0183* create(GwLog *logger, GwBoatData *boatData, SendNMEA0183MessageCallback callback,
String talkerId, GwXDRMappings *xdrMappings,const Config &cfg); String talkerId, GwXDRMappings *xdrMappings,const GwConverterConfig &cfg);
virtual void HandleMsg(const tN2kMsg &N2kMsg, int sourceId) = 0; virtual void HandleMsg(const tN2kMsg &N2kMsg, int sourceId) = 0;
virtual void loop(); virtual void loop(unsigned long lastRmc);
virtual ~N2kDataToNMEA0183(){} virtual ~N2kDataToNMEA0183(){}
virtual unsigned long* handledPgns()=0; virtual unsigned long* handledPgns()=0;
virtual int numPgns()=0; virtual int numPgns()=0;

View File

@ -808,10 +808,8 @@ void setup() {
webserver.begin(); webserver.begin();
xdrMappings.begin(); xdrMappings.begin();
logger.flush(); logger.flush();
N2kDataToNMEA0183::Config n2kTo183cfg; GwConverterConfig converterConfig;
n2kTo183cfg.minXdrInterval=config.getInt(config.minXdrInterval,100); converterConfig.init(&config);
n2kTo183cfg.starboardRudderInstance=config.getInt(config.stbRudderI,0);
n2kTo183cfg.portRudderInstance=config.getInt(config.portRudderI,-1);
nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData, nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData,
[](const tNMEA0183Msg &msg, int sourceId){ [](const tNMEA0183Msg &msg, int sourceId){
SendNMEA0183Message(msg,sourceId,false); SendNMEA0183Message(msg,sourceId,false);
@ -819,7 +817,7 @@ void setup() {
, ,
config.getString(config.talkerId,String("GP")), config.getString(config.talkerId,String("GP")),
&xdrMappings, &xdrMappings,
n2kTo183cfg converterConfig
); );
toN2KConverter= NMEA0183DataToN2K::create(&logger,&boatData,[](const tN2kMsg &msg, int sourceId)->bool{ toN2KConverter= NMEA0183DataToN2K::create(&logger,&boatData,[](const tN2kMsg &msg, int sourceId)->bool{
@ -828,7 +826,7 @@ void setup() {
return true; return true;
}, },
&xdrMappings, &xdrMappings,
config.getInt(config.min2KInterval,50) converterConfig
); );
NMEA2000.SetN2kCANMsgBufSize(8); NMEA2000.SetN2kCANMsgBufSize(8);
@ -950,7 +948,8 @@ void loopRun() {
preferences.end(); preferences.end();
logger.logDebug(GwLog::LOG,"Address Change: New Address=%d\n", SourceAddress); logger.logDebug(GwLog::LOG,"Address Change: New Address=%d\n", SourceAddress);
} }
nmea0183Converter->loop(); //potentially send out an own RMC if we did not receive one
nmea0183Converter->loop(toN2KConverter->getLastRmc());
monitor.setTime(8); monitor.setTime(8);
//read channels //read channels

View File

@ -8,6 +8,86 @@
"description": "system name, used for the access point and for services", "description": "system name, used for the access point and for services",
"category": "system" "category": "system"
}, },
{
"name": "stopApTime",
"type": "number",
"default": "0",
"check": "checkMinMax",
"description": "stop the access point after that many minutes if not used",
"category": "system"
},
{
"name": "apPassword",
"type": "password",
"default": "esp32nmea2k",
"check": "checkApPass",
"description": "set the password for the Wifi access point",
"category": "system",
"capabilities":{"apPwChange":["true"]}
},
{
"name": "apIp",
"type": "string",
"default":"192.168.15.1",
"check": "checkApIp",
"description": "The IP address for the access point. Clients will get addresses within the same subnet.",
"category":"system"
},
{
"name": "apMask",
"type": "string",
"default":"255.255.255.0",
"check": "checkNetMask",
"description": "The net mask for the access point",
"category":"system"
},
{
"name": "useAdminPass",
"type": "boolean",
"default": "true",
"description": "use a password for config modifications",
"category": "system"
},
{
"name": "adminPassword",
"type": "password",
"default": "esp32admin",
"check": "checkAdminPass",
"description": "set the password for config modifications",
"category": "system"
},
{
"name": "showInvalidData",
"label": "show all data",
"type": "boolean",
"default": "true",
"description": "show also not received items on data page",
"category": "system"
},
{
"name":"logLevel",
"label": "log level",
"type":"list",
"default":"0",
"list": [
{"l":"off","v":"-1"},
{"l":"error","v":"0"},
{"l":"log","v":"1"},
{"l":"debug","v":"3"}
],
"description": "log level at the USB port",
"category":"system"
},
{
"name":"ledBrightness",
"label":"led brightness",
"type":"number",
"default":64,
"min":0,
"max":255,
"description":"the brightness of the led (0..255)",
"category":"system"
},
{ {
"name": "talkerId", "name": "talkerId",
"label": "NMEA0183 ID", "label": "NMEA0183 ID",
@ -100,87 +180,7 @@
"ZV" "ZV"
], ],
"description": "the talkerId used in generated NMEA0183 records", "description": "the talkerId used in generated NMEA0183 records",
"category": "system" "category": "converter"
},
{
"name": "stopApTime",
"type": "number",
"default": "0",
"check": "checkMinMax",
"description": "stop the access point after that many minutes if not used",
"category": "system"
},
{
"name": "apPassword",
"type": "password",
"default": "esp32nmea2k",
"check": "checkApPass",
"description": "set the password for the Wifi access point",
"category": "system",
"capabilities":{"apPwChange":["true"]}
},
{
"name": "apIp",
"type": "string",
"default":"192.168.15.1",
"check": "checkApIp",
"description": "The IP address for the access point. Clients will get addresses within the same subnet.",
"category":"system"
},
{
"name": "apMask",
"type": "string",
"default":"255.255.255.0",
"check": "checkNetMask",
"description": "The net mask for the access point",
"category":"system"
},
{
"name": "useAdminPass",
"type": "boolean",
"default": "true",
"description": "use a password for config modifications",
"category": "system"
},
{
"name": "adminPassword",
"type": "password",
"default": "esp32admin",
"check": "checkAdminPass",
"description": "set the password for config modifications",
"category": "system"
},
{
"name": "showInvalidData",
"label": "show all data",
"type": "boolean",
"default": "true",
"description": "show also not received items on data page",
"category": "system"
},
{
"name":"logLevel",
"label": "log level",
"type":"list",
"default":"0",
"list": [
{"l":"off","v":"-1"},
{"l":"error","v":"0"},
{"l":"log","v":"1"},
{"l":"debug","v":"3"}
],
"description": "log level at the USB port",
"category":"system"
},
{
"name":"ledBrightness",
"label":"led brightness",
"type":"number",
"default":64,
"min":0,
"max":255,
"description":"the brightness of the led (0..255)",
"category":"system"
}, },
{ {
"name": "minXdrInterval", "name": "minXdrInterval",
@ -210,6 +210,24 @@
"description":"send out the converted data on the NMEA2000 bus\nIf set to off the converted data will still be shown at the data tab.", "description":"send out the converted data on the NMEA2000 bus\nIf set to off the converted data will still be shown at the data tab.",
"category":"converter" "category":"converter"
}, },
{
"name":"sendRMCi",
"label":"send RMC interval",
"type": "number",
"description":"interval (ms) to automatically send an RMC if we have valid position data (min 100ms, set to 0 to disable)",
"default":"1000",
"category":"converter"
},
{
"name":"checkRMCt",
"label": "check RMC time",
"type": "number",
"description": "start sending RMC if we did not see an external RMC after this much ms",
"default":"4000",
"min": 1000,
"check":"checkMinMax",
"category":"converter"
},
{ {
"name": "stbRudderI", "name": "stbRudderI",
"label":"stb rudder instance", "label":"stb rudder instance",