Merge branch 'wellenvogel:master' into master
This commit is contained in:
commit
f00064ece8
19
Readme.md
19
Readme.md
|
@ -38,7 +38,8 @@ What is included
|
|||
* a WEB UI to configure the gateway and to show the data that has been received
|
||||
* a USB Actisense to NMEA2000 gateway
|
||||
* a NMEA2000 to USB Actisense gateway
|
||||
* starting with 201311xx some I2C Sensors
|
||||
* starting with 201311xx some [I2C Sensors](doc/Sensors.md)
|
||||
* starting with 20240324 [SSI rotary encoders](doc/Sensors.md)
|
||||
|
||||
For the details of the mapped PGNs and NMEA sentences refer to [Conversions](doc/Conversions.pdf).
|
||||
|
||||
|
@ -165,10 +166,26 @@ For details refer to the [example description](lib/exampletask/Readme.md).
|
|||
|
||||
Changelog
|
||||
---------
|
||||
[20240324](../../releases/tag/20240324)
|
||||
**********
|
||||
* add [SSI rotary encoders](doc/Sensors.md)
|
||||
* add some options to the converter (RMC rate, RSA parameters)
|
||||
* support for the [M5 Atomic PortABC](https://shop.m5stack.com/products/atomic-portabc-extension-base) - more grove ports [#58](../../issues/58)
|
||||
* some restructuring in the hardware definitions
|
||||
* add SSID to status page [#37](../../issues/37)
|
||||
* allow to attach i2c sensors to the grove ports in the [build service](doc/BuildService.md)
|
||||
* add calset and calval config types<br>
|
||||
This requires to write current values of a sensor at the [api](../lib/api/GwApi.h#L191) with setCalibrationValue.
|
||||
The user can then bring up a calibration dialog and can set the current value as the config value.
|
||||
* change log flushing to USB port that prevented ESP32 S3 based boards to start if no USB connection was available
|
||||
* prevent the Web UI from appearing frozen if there was a large amount of invalid NMEA data received [#60](../../issues/60)
|
||||
|
||||
|
||||
[20231228](../../releases/tag/20231228)
|
||||
**********
|
||||
* lock AsyncTCP-esphome to 2.0.1 to avoid compile errors
|
||||
* own main loop to avoid deadlocks with serial send in user tasks
|
||||
|
||||
[20231105](../../releases/tag/20231105)
|
||||
**********
|
||||
* support for ESP32-S3
|
||||
|
|
|
@ -3,6 +3,7 @@ Hardware Configurations
|
|||
This pages describes a couple of the potential hardware configurations you could use with the code.
|
||||
Finally this list is not complete and you can easily define your own set up by adding definitions.
|
||||
Hint: all prebuild binaries can be found at [releases](https://github.com/wellenvogel/esp32-nmea2000/releases).
|
||||
Additionally you can use the [online build service](BuildService.md) to create binaries for a lot of hardware combinations.
|
||||
The "Build Defines" describe which of the hardware definitions from [Hardware.h](../lib/hardware/Hardware.h) are used.
|
||||
|
||||
M5 Atom CAN
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
Sensors
|
||||
=======
|
||||
The software contains support for a couple of sensors (starting from [20231228](../../releases/tag/20231228) and extend in consecutive releases).
|
||||
This includes some I2C Sensors and SSI rotary encoders.
|
||||
To connect sensors the following steps are necessary:
|
||||
|
||||
1. Check for the supported sensors and decide which base you would like to use. For i2c sensors you can typically connect a couple of them to one i2c bus - so the M5 grove connector would suit this well. If you are not directly using the M5 modules you may need to check the voltages: The M5 grove carries 5V but the logic levels are 3.3V.
|
||||
Check e.g. the [description of the M5 atom lite](https://docs.m5stack.com/en/core/atom_lite) for the pinout of the grove port.
|
||||
|
||||
2. Use the [online build service](BuildService.md) to create a binary that matches your configuration and flash it.
|
||||
|
||||
3. Use the configuration to fine tune the parameters for the NMEA2000 messages and the NMEA0183 messages. Potentially you also can adjust some calibration values.
|
||||
|
||||
Configuration and Measure Flow
|
||||
------------------------------
|
||||
During the building of the binary a couple of compile flags will control which buses (i2c, spi) and which sensors will be built into the binary.
|
||||
On startup the software will try to initialize the sensors and will start the periodic measurement tasks.
|
||||
Measured sensor data will always be sent as NMEA2000. For each sensor you can additionally define some mapping to NMEA0183 XDR transducer values (if there is no native NMEA0183 record for them).
|
||||
To ensure that the sensor data will be shown at the "data" page this conversion is necessary (i.e. if you disable this conversion sensor data will still be sent to the NMEA2000 bus - but will not be shown at the data page).
|
||||
For each sensor you can separately enable and disable it in the configuration.
|
||||
You can also typically select the NMEA2000 instance id for the sensor value and the measurement interval.
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
Sensors are implemented in [iictask](../lib/iictask) and in [spitaks](../lib/spitask/).
|
||||
They are implemented as [usertasks](../lib/exampletask/Readme.md).
|
||||
|
||||
Bus Usage
|
||||
---------
|
||||
When selecting sensors to be connected at the M5 grove ports in the [online build service](BuildService.md) the system will select the appropriate bus (i2c-1, i2c-2) by it's own. As you can have up to 4 grove ports (one at the device and 3 by using the [M5 Atomic PortABC](https://shop.m5stack.com/products/atomic-portabc-extension-base)) you can use both available i2c buses (and still utilize other groves for serial or CAN).
|
||||
|
||||
Implemented Sensors
|
||||
-------------------
|
||||
* [BME280](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf): temperature/humidity/pressure [PGNs: 130314,130312, 130313]
|
||||
* [QMP6988](https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/enviii/QMP6988%20Datasheet.pdf): pressure [PGN: 130314]
|
||||
* [SHT30](https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/SHT3x_Datasheet_digital.pdf): temperature and humidity [PGNs: 130312, 130313]
|
||||
* [M5-ENV3](https://docs.m5stack.com/en/unit/envIII): combination of QMP6988 and SHT30 [PGNs: 130314,130312, 130313]
|
||||
* [DMS22B](https://www.mouser.de/datasheet/2/54/bour_s_a0011704065_1-2262614.pdf)[since 20240324]: SSI rotary encoder (needs level shifters to / from 5V!) [PGN: 127245]
|
|
@ -22,6 +22,8 @@ CFG_INCLUDE='GwConfigDefinitions.h'
|
|||
CFG_INCLUDE_IMPL='GwConfigDefImpl.h'
|
||||
XDR_INCLUDE='GwXdrTypeMappings.h'
|
||||
TASK_INCLUDE='GwUserTasks.h'
|
||||
GROVE_CONFIG="GwM5GroveGen.h"
|
||||
GROVE_CONFIG_IN="lib/hardware/GwM5Grove.in"
|
||||
EMBEDDED_INCLUDE="GwEmbeddedFiles.h"
|
||||
|
||||
def getEmbeddedFiles(env):
|
||||
|
@ -74,7 +76,7 @@ def generateFile(infile,outfile,callback,inMode='rb',outMode='w'):
|
|||
print("creating %s"%outfile)
|
||||
oh=None
|
||||
with open(infile,inMode) as ch:
|
||||
with open(outfile,'w') as oh:
|
||||
with open(outfile,outMode) as oh:
|
||||
try:
|
||||
callback(ch,oh,inFile=infile)
|
||||
oh.close()
|
||||
|
@ -185,34 +187,13 @@ def generateCfg(inFile,outFile,impl):
|
|||
name=item.get('name')
|
||||
if name is None:
|
||||
continue
|
||||
data+=' configs[%d]=\n'%(idx)
|
||||
data+=' configs[%d]='%(idx)
|
||||
idx+=1
|
||||
secret="false";
|
||||
if item.get('type') == 'password':
|
||||
secret="true"
|
||||
data+=" #undef __CFGMODE\n"
|
||||
data+=" #ifdef CFGMODE_%s\n"%(name)
|
||||
data+=" #define __CFGMODE CFGMODE_%s\n"%(name)
|
||||
data+=" #else\n"
|
||||
data+=" #define __CFGMODE GwConfigInterface::NORMAL\n"
|
||||
data+=" #endif\n"
|
||||
data+=" #ifdef CFGDEFAULT_%s\n"%(name)
|
||||
data+=" new GwConfigInterface(%s,CFGDEFAULT_%s,%s,__CFGMODE)\n"%(name,name,secret)
|
||||
data+=" #else\n"
|
||||
data+=" new GwConfigInterface(%s,\"%s\",%s,__CFGMODE)\n"%(name,item.get('default'),secret)
|
||||
data+=" #endif\n"
|
||||
data+=";\n"
|
||||
data+=" new GwConfigInterface(%s,\"%s\",%s);\n"%(name,item.get('default'),secret)
|
||||
data+='}\n'
|
||||
for item in config:
|
||||
name=item.get('name')
|
||||
if name is None:
|
||||
continue
|
||||
data+="#ifdef CFGMODE_%s\n"%(name)
|
||||
data+=" __MSG(\"CFGMODE_%s=\" __XSTR(CFGMODE_%s))\n"%(name,name)
|
||||
data+="#endif\n"
|
||||
data+="#ifdef CFGDEFAULT_%s\n"%(name)
|
||||
data+=" __MSG(\"CFGDEFAULT_%s=\" CFGDEFAULT_%s)\n"%(name,name)
|
||||
data+="#endif\n"
|
||||
writeFileIfChanged(outFile,data)
|
||||
|
||||
def labelFilter(label):
|
||||
|
@ -284,6 +265,41 @@ def generateXdrMappings(fp,oh,inFile=''):
|
|||
oh.write(" #define %s %s\n"%(define,str(v)))
|
||||
idx+=1
|
||||
|
||||
class Grove:
|
||||
def __init__(self,name) -> None:
|
||||
self.name=name
|
||||
def _ss(self,z=False):
|
||||
if z:
|
||||
return self.name
|
||||
return self.name if self.name is not 'Z' else ''
|
||||
def _suffix(self):
|
||||
return '_'+self.name if self.name is not 'Z' else ''
|
||||
def replace(self,line):
|
||||
if line is None:
|
||||
return line
|
||||
return line.replace('$G$',self._ss()).replace('$Z$',self._ss(True)).replace('$GS$',self._suffix())
|
||||
def generateGroveDefs(inh,outh,inFile=''):
|
||||
GROVES=[Grove('Z'),Grove('A'),Grove('B'),Grove('C')]
|
||||
definition=[]
|
||||
started=False
|
||||
def writeConfig():
|
||||
for grove in GROVES:
|
||||
for cl in definition:
|
||||
outh.write(grove.replace(cl))
|
||||
|
||||
for line in inh:
|
||||
if re.match(" *#GROVE",line):
|
||||
started=True
|
||||
if len(definition) > 0:
|
||||
writeConfig()
|
||||
definition=[]
|
||||
continue
|
||||
if started:
|
||||
definition.append(line)
|
||||
if len(definition) > 0:
|
||||
writeConfig()
|
||||
|
||||
|
||||
|
||||
userTaskDirs=[]
|
||||
|
||||
|
@ -444,6 +460,7 @@ def prebuild(env):
|
|||
generateEmbedded(filedefs,os.path.join(outPath(),EMBEDDED_INCLUDE))
|
||||
genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE))
|
||||
generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings)
|
||||
generateFile(os.path.join(basePath(),GROVE_CONFIG_IN),os.path.join(outPath(),GROVE_CONFIG),generateGroveDefs,inMode='r')
|
||||
version="dev"+datetime.now().strftime("%Y%m%d")
|
||||
env.Append(CPPDEFINES=[('GWDEVVERSION',version)])
|
||||
|
||||
|
|
|
@ -182,6 +182,13 @@ class GwApi{
|
|||
* The name should be similar to the function name of the user task (although not mandatory)
|
||||
*/
|
||||
virtual bool addUserTask(GwUserTaskFunction task,const String Name, int stackSize=2000)=0;
|
||||
/**
|
||||
* set a value that is used for calibration in config values
|
||||
* for cfg types calset, calvalue
|
||||
* @param name: the config name this value is used for
|
||||
* @param value: the current value
|
||||
*/
|
||||
virtual void setCalibrationValue(const String &name, double value);
|
||||
|
||||
/**
|
||||
* not thread safe methods
|
||||
|
|
|
@ -192,7 +192,8 @@ class GwBoatData{
|
|||
GWBOATDATA(double,HDOP,4000,formatDop)
|
||||
GWBOATDATA(double,PDOP,4000,formatDop)
|
||||
GWBOATDATA(double,VDOP,4000,formatDop)
|
||||
GWBOATDATA(double,RPOS,4000,formatCourse) //RudderPosition
|
||||
GWBOATDATA(double,RPOS,4000,formatWind) //RudderPosition
|
||||
GWBOATDATA(double,PRPOS,4000,formatWind) //second rudder pos
|
||||
GWBOATDATA(double,LAT,4000,formatLatitude)
|
||||
GWBOATDATA(double,LON,4000,formatLongitude)
|
||||
GWBOATDATA(double,ALT,4000,formatFixed0) //altitude
|
||||
|
|
|
@ -106,20 +106,34 @@ void GwChannel::setImpl(GwChannelInterface *impl){
|
|||
}
|
||||
void GwChannel::updateCounter(const char *msg, bool out)
|
||||
{
|
||||
char key[6];
|
||||
char key[7];
|
||||
key[0]=0;
|
||||
if (msg[0] == '$')
|
||||
{
|
||||
strncpy(key, &msg[3], 3);
|
||||
for (int i=0;i<6 && msg[i] != 0;i++){
|
||||
if (i>=3) {
|
||||
if (isalnum(msg[i]))key[i-3]=msg[i];
|
||||
else key[i-3]='_';
|
||||
}
|
||||
key[i-2]=0;
|
||||
}
|
||||
key[3] = 0;
|
||||
}
|
||||
else if (msg[0] == '!')
|
||||
{
|
||||
strncpy(key, &msg[1], 5);
|
||||
for (int i=0;i<6 && msg[i] != 0;i++){
|
||||
if (i>=1) {
|
||||
if (isalnum(msg[i]))key[i-1]=msg[i];
|
||||
else key[i-1]='_';
|
||||
}
|
||||
key[i]=0;
|
||||
}
|
||||
key[5] = 0;
|
||||
}
|
||||
else{
|
||||
return;
|
||||
}
|
||||
if (key[0] == 0) return;
|
||||
if (out){
|
||||
countOut->add(key);
|
||||
}
|
||||
|
@ -159,7 +173,7 @@ void GwChannel::toJson(GwJsonDocument &doc){
|
|||
if (countIn) countIn->toJson(doc);
|
||||
}
|
||||
String GwChannel::toString(){
|
||||
String rt="CH:"+name;
|
||||
String rt="CH"+name+"("+sourceId+"):";
|
||||
rt+=enabled?"[ena]":"[dis]";
|
||||
rt+=NMEAin?"in,":"";
|
||||
rt+=NMEAout?"out,":"";
|
||||
|
|
|
@ -1,9 +1,33 @@
|
|||
#include "GwChannelList.h"
|
||||
#include "GwApi.h"
|
||||
//check for duplicate groove usages
|
||||
#define GW_PINDEFS
|
||||
#include "GwHardware.h"
|
||||
#include "GwSocketServer.h"
|
||||
#include "GwSerial.h"
|
||||
#include "GwTcpClient.h"
|
||||
class SerInit{
|
||||
public:
|
||||
int serial=-1;
|
||||
int rx=-1;
|
||||
int tx=-1;
|
||||
int mode=-1;
|
||||
int fixedBaud=-1;
|
||||
SerInit(int s,int r,int t, int m, int b=-1):
|
||||
serial(s),rx(r),tx(t),mode(m),fixedBaud(b){}
|
||||
};
|
||||
std::vector<SerInit> serialInits;
|
||||
|
||||
#define CFG_SERIAL(ser,...) \
|
||||
__MSG("serial config " #ser); \
|
||||
static GwInitializer<SerInit> __serial ## ser ## _init \
|
||||
(serialInits,SerInit(ser,__VA_ARGS__));
|
||||
#ifdef _GWI_SERIAL1
|
||||
CFG_SERIAL(1,_GWI_SERIAL1)
|
||||
#endif
|
||||
#ifdef _GWI_SERIAL2
|
||||
CFG_SERIAL(2,_GWI_SERIAL2)
|
||||
#endif
|
||||
class GwSerialLog : public GwLogWriter
|
||||
{
|
||||
static const size_t bufferSize = 4096;
|
||||
|
@ -11,12 +35,13 @@ class GwSerialLog : public GwLogWriter
|
|||
int wp = 0;
|
||||
GwSerial *writer;
|
||||
bool disabled = false;
|
||||
|
||||
long flushTimeout=200;
|
||||
public:
|
||||
GwSerialLog(GwSerial *writer, bool disabled)
|
||||
GwSerialLog(GwSerial *writer, bool disabled,long flushTimeout=200)
|
||||
{
|
||||
this->writer = writer;
|
||||
this->disabled = disabled;
|
||||
this->flushTimeout=flushTimeout;
|
||||
logBuffer = new char[bufferSize];
|
||||
wp = 0;
|
||||
}
|
||||
|
@ -39,16 +64,63 @@ public:
|
|||
{
|
||||
while (handled < wp)
|
||||
{
|
||||
writer->flush();
|
||||
if ( !writer->flush(flushTimeout)) break;
|
||||
size_t rt = writer->sendToClients(logBuffer + handled, -1, true);
|
||||
handled += rt;
|
||||
}
|
||||
if (handled < wp){
|
||||
if (handled > 0){
|
||||
memmove(logBuffer,logBuffer+handled,wp-handled);
|
||||
wp-=handled;
|
||||
logBuffer[handled]=0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
wp = 0;
|
||||
logBuffer[0] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class SerialWrapper : public GwChannelList::SerialWrapperBase{
|
||||
private:
|
||||
template<class C>
|
||||
void beginImpl(C *s,unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1){}
|
||||
void beginImpl(HardwareSerial *s,unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1){
|
||||
s->begin(baud,config,rxPin,txPin);
|
||||
}
|
||||
template<class C>
|
||||
void setError(C* s, GwLog *logger){}
|
||||
void setError(HardwareSerial *s,GwLog *logger){
|
||||
LOG_DEBUG(GwLog::LOG,"enable serial errors for channel %d",id);
|
||||
s->onReceiveError([logger,this](hardwareSerial_error_t err){
|
||||
LOG_DEBUG(GwLog::ERROR,"serial error on id %d: %d",this->id,(int)err);
|
||||
});
|
||||
}
|
||||
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||
void beginImpl(HWCDC *s,unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1){
|
||||
s->begin(baud);
|
||||
}
|
||||
#endif
|
||||
T *serial;
|
||||
int id;
|
||||
public:
|
||||
SerialWrapper(T* s,int i):serial(s),id(i){}
|
||||
virtual void begin(GwLog* logger,unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1) override{
|
||||
beginImpl(serial,baud,config,rxPin,txPin);
|
||||
setError(serial,logger);
|
||||
};
|
||||
virtual Stream *getStream() override{
|
||||
return serial;
|
||||
}
|
||||
virtual int getId() override{
|
||||
return id;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
GwChannelList::GwChannelList(GwLog *logger, GwConfigHandler *config){
|
||||
this->logger=logger;
|
||||
this->config=config;
|
||||
|
@ -101,8 +173,18 @@ static SerialParam *getSerialParam(int id){
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GwChannelList:: addSerial(HardwareSerial *stream,int id,int type,int rx,int tx){
|
||||
void GwChannelList::addSerial(int id, int rx, int tx, int type){
|
||||
if (id == 1){
|
||||
addSerial(new SerialWrapper<decltype(Serial1)>(&Serial1,SERIAL1_CHANNEL_ID),type,rx,tx);
|
||||
return;
|
||||
}
|
||||
if (id == 2){
|
||||
addSerial(new SerialWrapper<decltype(Serial2)>(&Serial2,SERIAL2_CHANNEL_ID),type,rx,tx);
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(GwLog::ERROR,"invalid serial config with id %d",id);
|
||||
}
|
||||
void GwChannelList::addSerial(GwChannelList::SerialWrapperBase *stream,int type,int rx,int tx){
|
||||
const char *mode=nullptr;
|
||||
switch (type)
|
||||
{
|
||||
|
@ -123,9 +205,16 @@ void GwChannelList:: addSerial(HardwareSerial *stream,int id,int type,int rx,int
|
|||
LOG_DEBUG(GwLog::ERROR,"unknown serial type %d",type);
|
||||
return;
|
||||
}
|
||||
addSerial(stream,id,mode,rx,tx);
|
||||
addSerial(stream,mode,rx,tx);
|
||||
}
|
||||
void GwChannelList::addSerial(GwChannelList::SerialWrapperBase *serialStream,const String &mode,int rx,int tx){
|
||||
int id=serialStream->getId();
|
||||
for (auto &&it:theChannels){
|
||||
if (it->isOwnSource(id)){
|
||||
LOG_DEBUG(GwLog::ERROR,"trying to re-add serial id=%d, ignoring",id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
void GwChannelList::addSerial(HardwareSerial *serialStream,int id,const String &mode,int rx,int tx){
|
||||
SerialParam *param=getSerialParam(id);
|
||||
if (param == nullptr){
|
||||
logger->logDebug(GwLog::ERROR,"trying to set up an unknown serial channel: %d",id);
|
||||
|
@ -161,8 +250,8 @@ void GwChannelList::addSerial(HardwareSerial *serialStream,int id,const String &
|
|||
if (tx < 0) canWrite=false;
|
||||
LOG_DEBUG(GwLog::DEBUG,"serial set up: mode=%s,rx=%d,canRead=%d,tx=%d,canWrite=%d",
|
||||
mode.c_str(),rx,(int)canRead,tx,(int)canWrite);
|
||||
serialStream->begin(config->getInt(param->baud,115200),SERIAL_8N1,rx,tx);
|
||||
GwSerial *serial = new GwSerial(logger, serialStream, id, canRead);
|
||||
serialStream->begin(logger,config->getInt(param->baud,115200),SERIAL_8N1,rx,tx);
|
||||
GwSerial *serial = new GwSerial(logger, serialStream->getStream(), id, canRead);
|
||||
LOG_DEBUG(GwLog::LOG, "starting serial %d ", id);
|
||||
GwChannel *channel = new GwChannel(logger, param->name, id);
|
||||
channel->setImpl(serial);
|
||||
|
@ -179,7 +268,36 @@ void GwChannelList::addSerial(HardwareSerial *serialStream,int id,const String &
|
|||
LOG_DEBUG(GwLog::LOG, "%s", channel->toString().c_str());
|
||||
theChannels.push_back(channel);
|
||||
}
|
||||
|
||||
void GwChannelList::preinit(){
|
||||
for (auto &&init:serialInits){
|
||||
if (init.fixedBaud >= 0){
|
||||
switch(init.serial){
|
||||
case 1:
|
||||
{
|
||||
LOG_DEBUG(GwLog::DEBUG,"setting fixed baud %d for serial",init.fixedBaud);
|
||||
config->setValue(GwConfigDefinitions::serialBaud,String(init.fixedBaud),GwConfigInterface::READONLY);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
LOG_DEBUG(GwLog::DEBUG,"setting fixed baud %d for serial2",init.fixedBaud);
|
||||
config->setValue(GwConfigDefinitions::serial2Baud,String(init.fixedBaud),GwConfigInterface::READONLY);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_DEBUG(GwLog::ERROR,"invalid serial definition %d found",init.serial)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
template<typename S>
|
||||
long getFlushTimeout(S &s){
|
||||
return 200;
|
||||
}
|
||||
template<>
|
||||
long getFlushTimeout(HardwareSerial &s){
|
||||
return 2000;
|
||||
}
|
||||
void GwChannelList::begin(bool fallbackSerial){
|
||||
LOG_DEBUG(GwLog::DEBUG,"GwChannelList::begin");
|
||||
GwChannel *channel=NULL;
|
||||
|
@ -187,7 +305,7 @@ void GwChannelList::begin(bool fallbackSerial){
|
|||
if (! fallbackSerial){
|
||||
GwSerial *usb=new GwSerial(NULL,&USBSerial,USB_CHANNEL_ID);
|
||||
USBSerial.begin(config->getInt(config->usbBaud));
|
||||
logger->setWriter(new GwSerialLog(usb,config->getBool(config->usbActisense)));
|
||||
logger->setWriter(new GwSerialLog(usb,config->getBool(config->usbActisense),getFlushTimeout(USBSerial)));
|
||||
logger->prefix="GWSERIAL:";
|
||||
channel=new GwChannel(logger,"USB",USB_CHANNEL_ID);
|
||||
channel->setImpl(usb);
|
||||
|
@ -223,6 +341,11 @@ void GwChannelList::begin(bool fallbackSerial){
|
|||
LOG_DEBUG(GwLog::LOG,"%s",channel->toString().c_str());
|
||||
theChannels.push_back(channel);
|
||||
|
||||
//new serial config handling
|
||||
for (auto &&init:serialInits){
|
||||
addSerial(init.serial,init.rx,init.tx,init.mode);
|
||||
}
|
||||
//handle separate defines
|
||||
//serial 1
|
||||
#ifndef GWSERIAL_TX
|
||||
#define GWSERIAL_TX -1
|
||||
|
@ -231,10 +354,10 @@ void GwChannelList::begin(bool fallbackSerial){
|
|||
#define GWSERIAL_RX -1
|
||||
#endif
|
||||
#ifdef GWSERIAL_TYPE
|
||||
addSerial(&Serial1,SERIAL1_CHANNEL_ID,GWSERIAL_TYPE,GWSERIAL_RX,GWSERIAL_TX);
|
||||
addSerial(new SerialWrapper<decltype(Serial1)>(&Serial1,SERIAL1_CHANNEL_ID),GWSERIAL_TYPE,GWSERIAL_RX,GWSERIAL_TX);
|
||||
#else
|
||||
#ifdef GWSERIAL_MODE
|
||||
addSerial(&Serial1,SERIAL1_CHANNEL_ID,GWSERIAL_MODE,GWSERIAL_RX,GWSERIAL_TX);
|
||||
addSerial(new SerialWrapper<decltype(Serial1)>(&Serial1,SERIAL1_CHANNEL_ID),GWSERIAL_MODE,GWSERIAL_RX,GWSERIAL_TX);
|
||||
#endif
|
||||
#endif
|
||||
//serial 2
|
||||
|
@ -245,10 +368,10 @@ void GwChannelList::begin(bool fallbackSerial){
|
|||
#define GWSERIAL2_RX -1
|
||||
#endif
|
||||
#ifdef GWSERIAL2_TYPE
|
||||
addSerial(&Serial2,SERIAL2_CHANNEL_ID,GWSERIAL2_TYPE,GWSERIAL2_RX,GWSERIAL2_TX);
|
||||
addSerial(new SerialWrapper<decltype(Serial2)>(&Serial2,SERIAL2_CHANNEL_ID),GWSERIAL2_TYPE,GWSERIAL2_RX,GWSERIAL2_TX);
|
||||
#else
|
||||
#ifdef GWSERIAL2_MODE
|
||||
addSerial(&Serial2,SERIAL2_CHANNEL_ID,GWSERIAL2_MODE,GWSERIAL2_RX,GWSERIAL2_TX);
|
||||
addSerial(new SerialWrapper<decltype(Serial2)>(&Serial2,SERIAL2_CHANNEL_ID),GWSERIAL2_MODE,GWSERIAL2_RX,GWSERIAL2_TX);
|
||||
#endif
|
||||
#endif
|
||||
//tcp client
|
||||
|
|
|
@ -23,6 +23,12 @@ class GwSocketServer;
|
|||
class GwTcpClient;
|
||||
class GwChannelList{
|
||||
private:
|
||||
class SerialWrapperBase{
|
||||
public:
|
||||
virtual void begin(GwLog* logger,unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1)=0;
|
||||
virtual Stream *getStream()=0;
|
||||
virtual int getId()=0;
|
||||
};
|
||||
GwLog *logger;
|
||||
GwConfigHandler *config;
|
||||
typedef std::vector<GwChannel *> ChannelList;
|
||||
|
@ -30,12 +36,16 @@ class GwChannelList{
|
|||
std::map<int,String> modes;
|
||||
GwSocketServer *sockets;
|
||||
GwTcpClient *client;
|
||||
void addSerial(HardwareSerial *stream,int id,const String &mode,int rx,int tx);
|
||||
void addSerial(HardwareSerial *stream,int id,int type,int rx,int tx);
|
||||
void addSerial(SerialWrapperBase *stream,const String &mode,int rx,int tx);
|
||||
void addSerial(SerialWrapperBase *stream,int type,int rx,int tx);
|
||||
public:
|
||||
void addSerial(int id, int rx, int tx, int type);
|
||||
GwChannelList(GwLog *logger, GwConfigHandler *config);
|
||||
typedef std::function<void(GwChannel *)> ChannelAction;
|
||||
void allChannels(ChannelAction action);
|
||||
//called to allow setting some predefined configs
|
||||
//e.g. from serial definitions
|
||||
void preinit();
|
||||
//initialize
|
||||
void begin(bool fallbackSerial=false);
|
||||
//status
|
||||
|
|
|
@ -4,6 +4,13 @@
|
|||
#include <ArduinoJson.h>
|
||||
#include <string.h>
|
||||
#include <MD5Builder.h>
|
||||
using CfgInit=std::function<void(GwConfigHandler *)>;
|
||||
static std::vector<CfgInit> cfgInits;
|
||||
#define CFG_INIT(name,value,mode) \
|
||||
__MSG("config set " #name " " #value " " #mode); \
|
||||
static GwInitializer<CfgInit> _ ## name ## _init(cfgInits,[](GwConfigHandler *cfg){ \
|
||||
cfg->setValue(GwConfigDefinitions::name,value,GwConfigInterface::mode); \
|
||||
});
|
||||
#include "GwHardware.h"
|
||||
#include "GwConfigDefImpl.h"
|
||||
|
||||
|
@ -61,6 +68,9 @@ GwConfigHandler::GwConfigHandler(GwLog *logger): GwConfigDefinitions(){
|
|||
saltBase=esp_random();
|
||||
configs=new GwConfigInterface*[getNumConfig()];
|
||||
populateConfigs(configs);
|
||||
for (auto &&init:cfgInits){
|
||||
init(this);
|
||||
}
|
||||
prefs=new Preferences();
|
||||
}
|
||||
GwConfigHandler::~GwConfigHandler(){
|
||||
|
@ -126,11 +136,16 @@ void GwConfigHandler::stopChanges(){
|
|||
allowChanges=false;
|
||||
}
|
||||
bool GwConfigHandler::setValue(String name,String value, bool hide){
|
||||
return setValue(name,value,hide?GwConfigInterface::HIDDEN:GwConfigInterface::READONLY);
|
||||
}
|
||||
bool GwConfigHandler::setValue(String name, String value, GwConfigInterface::ConfigType type){
|
||||
if (! allowChanges) return false;
|
||||
LOG_DEBUG(GwLog::LOG,"setValue for %s to %s, mode %d",
|
||||
name.c_str(),value.c_str(),(int)type);
|
||||
GwConfigInterface *i=getConfigItem(name,false);
|
||||
if (!i) return false;
|
||||
i->value=value;
|
||||
i->type=hide?GwConfigInterface::HIDDEN:GwConfigInterface::READONLY;
|
||||
i->type=type;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ class GwConfigHandler: public GwConfigDefinitions{
|
|||
* !use with care! no checks of the value
|
||||
*/
|
||||
bool setValue(String name, String value, bool hide=false);
|
||||
bool setValue(String name, String value, GwConfigInterface::ConfigType type);
|
||||
static void toHex(unsigned long v,char *buffer,size_t bsize);
|
||||
unsigned long getSaltBase(){return saltBase;}
|
||||
~GwConfigHandler();
|
||||
|
@ -70,6 +71,15 @@ class GwConfigHandler: public GwConfigDefinitions{
|
|||
target=i->asInt();
|
||||
return true;
|
||||
}
|
||||
bool getValue(float &target, const String &name, float defaultv=0){
|
||||
GwConfigInterface *i=getConfigItem(name);
|
||||
if (!i){
|
||||
target=defaultv;
|
||||
return false;
|
||||
}
|
||||
target=i->asFloat();
|
||||
return true;
|
||||
}
|
||||
bool getValue(bool &target, const String name, bool defaultv=false){
|
||||
GwConfigInterface *i=getConfigItem(name);
|
||||
if (!i){
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#define _GWCONFIGITEM_H
|
||||
#include "WString.h"
|
||||
#include <vector>
|
||||
|
||||
class GwConfigHandler;
|
||||
class GwConfigInterface{
|
||||
public:
|
||||
|
@ -37,6 +36,9 @@ class GwConfigInterface{
|
|||
virtual int asInt() const{
|
||||
return (int)value.toInt();
|
||||
}
|
||||
virtual float asFloat() const{
|
||||
return value.toFloat();
|
||||
}
|
||||
String getName() const{
|
||||
return name;
|
||||
}
|
||||
|
@ -75,5 +77,20 @@ class GwNmeaFilter{
|
|||
|
||||
#define __XSTR(x) __STR(x)
|
||||
#define __STR(x) #x
|
||||
#define __EXPAND(x,sf) x ## sf
|
||||
#define __EXPAND3(prfx,x,sf) prfx ## x ## sf
|
||||
#define __MSG(x) _Pragma (__STR(message (x)))
|
||||
//https://curiouser.cheshireeng.com/2014/08/19/c-compile-time-assert/
|
||||
#define CASSERT(predicate, text) _impl_CASSERT_LINE(predicate,__LINE__)
|
||||
#define _impl_PASTE(a,b) a##b
|
||||
#define _impl_CASSERT_LINE(predicate, line) typedef char _impl_PASTE(assertion_failed_CASSERT_,line)[(predicate)?1:-1];
|
||||
template<typename F>
|
||||
class GwInitializer{
|
||||
public:
|
||||
using List=std::vector<F>;
|
||||
GwInitializer(List &l,F f){
|
||||
l.push_back(f);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -116,6 +116,10 @@ bool GwWebServer::registerMainHandler(const char *url,RequestCreator creator){
|
|||
});
|
||||
return true;
|
||||
}
|
||||
bool GwWebServer::registerHandler(const char * url,GwWebServer::HandlerFunction handler){
|
||||
server->on(url,HTTP_GET,handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GwWebServer::registerPostHandler(const char *url, ArRequestHandlerFunction requestHandler,
|
||||
ArBodyHandlerFunction bodyHandler){
|
||||
|
|
|
@ -11,10 +11,12 @@ class GwWebServer{
|
|||
GwLog *logger;
|
||||
public:
|
||||
typedef GwRequestMessage *(RequestCreator)(AsyncWebServerRequest *request);
|
||||
using HandlerFunction=std::function<void(AsyncWebServerRequest *)>;
|
||||
GwWebServer(GwLog *logger, GwRequestQueue *queue,int port);
|
||||
~GwWebServer();
|
||||
void begin();
|
||||
bool registerMainHandler(const char *url,RequestCreator creator);
|
||||
bool registerHandler(const char * url,HandlerFunction handler);
|
||||
bool registerPostHandler(const char *url, ArRequestHandlerFunction requestHandler, ArBodyHandlerFunction bodyHandler);
|
||||
void handleAsyncWebRequest(AsyncWebServerRequest *request, GwRequestMessage *msg);
|
||||
AsyncWebServer * getServer(){return server;}
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
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
|
||||
|
||||
unfortunately there is some typo here: M5 uses GROVE for their connections
|
||||
but we have GROOVE here.
|
||||
But to maintain compatibility to older build commands we keep the (wrong) wording
|
||||
*/
|
||||
#ifdef _NOGWHARDWAREUT
|
||||
#error "you are not allowed to include GwHardware.h in your user task header"
|
||||
|
@ -25,6 +29,20 @@
|
|||
#include "GwAppInfo.h"
|
||||
#include "GwUserTasks.h"
|
||||
|
||||
#ifdef GW_PINDEFS
|
||||
#define GWRESOURCE_USE(RES,USER) \
|
||||
__MSG(#RES " used by " #USER) \
|
||||
static int _resourceUsed ## RES =1;
|
||||
#define __USAGE __MSG
|
||||
#else
|
||||
#define GWRESOURCE_USE(...)
|
||||
#define __USAGE(...)
|
||||
#endif
|
||||
|
||||
#ifndef CFG_INIT
|
||||
#define CFG_INIT(...)
|
||||
#endif
|
||||
|
||||
//general definitions for M5AtomLite
|
||||
//hint for groove pins:
|
||||
//according to some schematics the numbering is 1,2,3(VCC),4(GND)
|
||||
|
@ -42,8 +60,13 @@
|
|||
#define GWBUTTON_PULLUPDOWN
|
||||
#define BOARD_LEFT1 GPIO_NUM_22
|
||||
#define BOARD_LEFT2 GPIO_NUM_19
|
||||
#define BOARD_LEFT3 GPIO_NUM_23
|
||||
#define BOARD_LEFT4 GPIO_NUM_33
|
||||
#define BOARD_RIGHT1 GPIO_NUM_21
|
||||
#define BOARD_RIGHT2 GPIO_NUM_25
|
||||
#define USBSerial Serial
|
||||
#endif
|
||||
|
||||
//general definitiones for M5AtomS3
|
||||
#ifdef PLATFORM_BOARD_M5STACK_ATOMS3
|
||||
#define GROOVE_PIN_2 GPIO_NUM_2
|
||||
|
@ -59,8 +82,11 @@
|
|||
#define GWBUTTON_PULLUPDOWN
|
||||
#define BOARD_LEFT1 GPIO_NUM_5
|
||||
#define BOARD_LEFT2 GPIO_NUM_6
|
||||
#define BOARD_LEFT3 GPIO_NUM_7
|
||||
#define BOARD_LEFT4 GPIO_NUM_8
|
||||
#define BOARD_RIGHT1 GPIO_NUM_39
|
||||
#define BOARD_RIGHT2 GPIO_NUM_38
|
||||
#endif
|
||||
|
||||
//M5Stick C
|
||||
#ifdef PLATFORM_BOARD_M5STICK_C
|
||||
#define GROOVE_PIN_2 GPIO_NUM_32
|
||||
|
@ -78,13 +104,13 @@
|
|||
//150mA if we power from the bus
|
||||
#define N2K_LOAD_LEVEL 3
|
||||
//if using tail485
|
||||
#define SERIAL_GROOVE_485
|
||||
#define SERIAL_GROOVE_485 1
|
||||
//brightness 0...255
|
||||
#define GWLED_BRIGHTNESS 64
|
||||
#endif
|
||||
|
||||
#ifdef BOARD_M5ATOMS3
|
||||
#define M5_CAN_KIT
|
||||
#define M5_CAN_KIT 1
|
||||
//150mA if we power from the bus
|
||||
#define N2K_LOAD_LEVEL 3
|
||||
//if using tail485
|
||||
|
@ -94,33 +120,33 @@
|
|||
#endif
|
||||
|
||||
#ifdef BOARD_M5ATOM_CANUNIT
|
||||
#define M5_CANUNIT
|
||||
#define M5_CANUNIT 1
|
||||
#define GWLED_BRIGHTNESS 64
|
||||
//150mA if we power from the bus
|
||||
#define N2K_LOAD_LEVEL 3
|
||||
#endif
|
||||
|
||||
#ifdef BOARD_M5ATOMS3_CANUNIT
|
||||
#define M5_CANUNIT
|
||||
#define M5_CANUNIT 1
|
||||
#define GWLED_BRIGHTNESS 64
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef BOARD_M5ATOM_RS232_CANUNIT
|
||||
#define M5_CANUNIT
|
||||
#define M5_CANUNIT 1
|
||||
#define M5_SERIAL_KIT_232
|
||||
#define GWLED_BRIGHTNESS 64
|
||||
#endif
|
||||
|
||||
#ifdef BOARD_M5ATOM_RS485_CANUNIT
|
||||
#define M5_SERIAL_KIT_485
|
||||
#define M5_CANUNIT
|
||||
#define M5_CANUNIT 1
|
||||
#define GWLED_BRIGHTNESS 64
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef BOARD_M5STICK_CANUNIT
|
||||
#define M5_CANUNIT
|
||||
#define M5_CANUNIT 1
|
||||
#endif
|
||||
|
||||
#ifdef BOARD_HOMBERGER
|
||||
|
@ -136,137 +162,14 @@
|
|||
#define GWBUTTON_PULLUPDOWN
|
||||
#endif
|
||||
|
||||
//M5 Serial (Atomic RS232 Base)
|
||||
#ifdef M5_SERIAL_KIT_232
|
||||
#define _GWM5_BOARD
|
||||
#define GWSERIAL_TX BOARD_LEFT2
|
||||
#define GWSERIAL_RX BOARD_LEFT1
|
||||
#define GWSERIAL_TYPE GWSERIAL_TYPE_BI
|
||||
#endif
|
||||
#include "GwM5Base.h"
|
||||
#include "GwM5Grove.h"
|
||||
|
||||
//M5 Serial (Atomic RS485 Base)
|
||||
#ifdef M5_SERIAL_KIT_485
|
||||
#ifdef _GWM5_BOARD
|
||||
#error "can only define one M5 base"
|
||||
#endif
|
||||
#define _GWM5_BOARD
|
||||
#define GWSERIAL_TX BOARD_LEFT2
|
||||
#define GWSERIAL_RX BOARD_LEFT1
|
||||
#define GWSERIAL_TYPE GWSERIAL_TYPE_UNI
|
||||
#endif
|
||||
|
||||
//M5 GPS (Atomic GPS Base)
|
||||
#ifdef M5_GPS_KIT
|
||||
#ifdef _GWM5_BOARD
|
||||
#error "can only define one M5 base"
|
||||
#endif
|
||||
#define _GWM5_BOARD
|
||||
#define GWSERIAL_RX BOARD_LEFT1
|
||||
#define GWSERIAL_TYPE GWSERIAL_TYPE_RX
|
||||
#define CFGDEFAULT_serialBaud "9600"
|
||||
#define CFGMODE_serialBaud GwConfigInterface::READONLY
|
||||
#endif
|
||||
|
||||
//below we define the final device config based on the above
|
||||
//boards and peripherals
|
||||
//this allows us to easily also set them from outside
|
||||
//serial adapter at the M5 groove pins
|
||||
//we use serial2 for groove serial if serial1 is already defined
|
||||
//before (e.g. by serial kit)
|
||||
#ifdef SERIAL_GROOVE_485
|
||||
#define _GWM5_GROOVE
|
||||
#ifdef GWSERIAL_TYPE
|
||||
#define GWSERIAL2_TX GROOVE_PIN_2
|
||||
#define GWSERIAL2_RX GROOVE_PIN_1
|
||||
#define GWSERIAL2_TYPE GWSERIAL_TYPE_UNI
|
||||
#else
|
||||
#define GWSERIAL_TX GROOVE_PIN_2
|
||||
#define GWSERIAL_RX GROOVE_PIN_1
|
||||
#define GWSERIAL_TYPE GWSERIAL_TYPE_UNI
|
||||
#endif
|
||||
#endif
|
||||
#ifdef SERIAL_GROOVE_232
|
||||
#ifdef _GWM5_GROOVE
|
||||
#error "can only have one groove device"
|
||||
#endif
|
||||
#define _GWM5_GROOVE
|
||||
#ifdef GWSERIAL_TYPE
|
||||
#define GWSERIAL2_TX GROOVE_PIN_2
|
||||
#define GWSERIAL2_RX GROOVE_PIN_1
|
||||
#define GWSERIAL2_TYPE GWSERIAL_TYPE_BI
|
||||
#else
|
||||
#define GWSERIAL_TX GROOVE_PIN_2
|
||||
#define GWSERIAL_RX GROOVE_PIN_1
|
||||
#define GWSERIAL_TYPE GWSERIAL_TYPE_BI
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//http://docs.m5stack.com/en/unit/gps
|
||||
#ifdef M5_GPS_UNIT
|
||||
#ifdef _GWM5_GROOVE
|
||||
#error "can only have one M5 groove"
|
||||
#endif
|
||||
#define _GWM5_GROOVE
|
||||
#ifdef GWSERIAL_TYPE
|
||||
#define GWSERIAL2_RX GROOVE_PIN_1
|
||||
#define GWSERIAL2_TYPE GWSERIAL_TYPE_RX
|
||||
#define CFGDEFAULT_serialBaud "9600"
|
||||
#define CFGMODE_serialBaud GwConfigInterface::READONLY
|
||||
#else
|
||||
#define GWSERIAL_RX GROOVE_PIN_1
|
||||
#define GWSERIAL_TYPE GWSERIAL_TYPE_RX
|
||||
#define CFGDEFAULT_serial2Baud "9600"
|
||||
#define CFGMODE_serial2Baud GwConfigInterface::READONLY
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//can kit for M5 Atom
|
||||
#ifdef M5_CAN_KIT
|
||||
#ifdef _GWM5_BOARD
|
||||
#error "can only define one M5 base"
|
||||
#endif
|
||||
#define _GWM5_BOARD
|
||||
#define ESP32_CAN_TX_PIN BOARD_LEFT1
|
||||
#define ESP32_CAN_RX_PIN BOARD_LEFT2
|
||||
#endif
|
||||
//CAN via groove
|
||||
#ifdef M5_CANUNIT
|
||||
#ifdef _GWM5_GROOVE
|
||||
#error "can only have one M5 groove"
|
||||
#endif
|
||||
#define _GWM5_GROOVE
|
||||
#define ESP32_CAN_TX_PIN GROOVE_PIN_2
|
||||
#define ESP32_CAN_RX_PIN GROOVE_PIN_1
|
||||
#endif
|
||||
|
||||
#ifdef M5_ENV3
|
||||
#ifndef M5_GROOVEIIC
|
||||
#define M5_GROOVEIIC
|
||||
#endif
|
||||
#ifndef GWSHT3X
|
||||
#define GWSHT3X -1
|
||||
#endif
|
||||
#ifndef GWQMP6988
|
||||
#define GWQMP6988 -1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef M5_GROOVEIIC
|
||||
#ifdef _GWM5_GROOVE
|
||||
#error "can only have one M5 groove"
|
||||
#endif
|
||||
#define _GWM5_GROOVE
|
||||
#ifdef GWIIC_SCL
|
||||
#error "you cannot define both GWIIC_SCL and M5_GROOVEIIC"
|
||||
#endif
|
||||
#define GWIIC_SCL GROOVE_PIN_1
|
||||
#ifdef GWIIC_SDA
|
||||
#error "you cannot define both GWIIC_SDA and M5_GROOVEIIC"
|
||||
#endif
|
||||
#define GWIIC_SDA GROOVE_PIN_2
|
||||
#endif
|
||||
|
||||
#ifdef GWIIC_SDA
|
||||
#ifdef _GWI_IIC1
|
||||
#error "you must not define IIC1 on grove and GWIIC_SDA"
|
||||
#endif
|
||||
#ifndef GWIIC_SCL
|
||||
#error "you must both define GWIIC_SDA and GWIIC_SCL"
|
||||
#endif
|
||||
|
@ -278,6 +181,9 @@
|
|||
#define _GWIIC
|
||||
#endif
|
||||
#ifdef GWIIC_SDA2
|
||||
#ifdef _GWI_IIC2
|
||||
#error "you must not define IIC2 on grove and GWIIC_SDA2"
|
||||
#endif
|
||||
#ifndef GWIIC_SCL2
|
||||
#error "you must both define GWIIC_SDA2 and GWIIC_SCL2"
|
||||
#endif
|
||||
|
@ -314,12 +220,13 @@
|
|||
#endif
|
||||
|
||||
#ifdef GWLED_FASTLED
|
||||
#define CFGMODE_ledBrightness GwConfigInterface::NORMAL
|
||||
#ifdef GWLED_BRIGHTNESS
|
||||
#define CFGDEFAULT_ledBrightness GWSTRINGIFY(GWLED_BRIGHTNESS)
|
||||
CFG_INIT(ledBrightness,GWSTRINGIFY(GWLED_BRIGHTNESS),NORMAL)
|
||||
#else
|
||||
CFG_INIT(ledBrightness,"64",NORMAL)
|
||||
#endif
|
||||
#else
|
||||
#define CFGMODE_ledBrightness GwConfigInterface::HIDDEN
|
||||
CFG_INIT(ledBrightness,"64",HIDDEN)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
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
|
||||
|
||||
unfortunately there is some typo here: M5 uses GROVE for their connections
|
||||
but we have GROOVE here.
|
||||
But to maintain compatibility to older build commands we keep the (wrong) wording
|
||||
*/
|
||||
//M5 Base Boards
|
||||
#ifndef _GWM5BASE_H
|
||||
#define _GWM5BASE_H
|
||||
//M5 Serial (Atomic RS232 Base)
|
||||
#ifdef M5_SERIAL_KIT_232
|
||||
GWRESOURCE_USE(BASE,M5_SERIAL_KIT_232)
|
||||
GWRESOURCE_USE(SERIAL1,M5_SERIAL_KIT_232)
|
||||
#define _GWI_SERIAL1 BOARD_LEFT1,BOARD_LEFT2,GWSERIAL_TYPE_BI
|
||||
#endif
|
||||
|
||||
//M5 Serial (Atomic RS485 Base)
|
||||
#ifdef M5_SERIAL_KIT_485
|
||||
GWRESOURCE_USE(BASE,M5_SERIAL_KIT_485)
|
||||
GWRESOURCE_USE(SERIAL1,M5_SERIAL_KIT_485)
|
||||
#define _GWI_SERIAL1 BOARD_LEFT1,BOARD_LEFT2,GWSERIAL_TYPE_UNI
|
||||
#endif
|
||||
//M5 GPS (Atomic GPS Base)
|
||||
#ifdef M5_GPS_KIT
|
||||
GWRESOURCE_USE(BASE,M5_GPS_KIT)
|
||||
GWRESOURCE_USE(SERIAL1,M5_GPS_KIT)
|
||||
#define _GWI_SERIAL1 BOARD_LEFT1,-1,GWSERIAL_TYPE_UNI,"9600"
|
||||
#endif
|
||||
|
||||
//M5 ProtoHub
|
||||
#ifdef M5_PROTO_HUB
|
||||
GWRESOURCE_USE(BASE,M5_PROTO_HUB)
|
||||
#define PPIN22 BOARD_LEFT1
|
||||
#define PPIN19 BOARD_LEFT2
|
||||
#define PPIN23 BOARD_LEFT3
|
||||
#define PPIN33 BOARD_LEFT4
|
||||
#define PPIN21 BOARD_RIGHT1
|
||||
#define PPIN25 BOARD_RIGHT2
|
||||
#endif
|
||||
|
||||
//M5 PortABC extension
|
||||
#ifdef M5_PORTABC
|
||||
GWRESOURCE_USE(BASE,M5_PORTABC)
|
||||
#define GROOVEA_PIN_2 BOARD_RIGHT2
|
||||
#define GROOVEA_PIN_1 BOARD_RIGHT1
|
||||
#define GROOVEB_PIN_2 BOARD_LEFT3
|
||||
#define GROOVEB_PIN_1 BOARD_LEFT4
|
||||
#define GROOVEC_PIN_2 BOARD_LEFT1
|
||||
#define GROOVEC_PIN_1 BOARD_LEFT2
|
||||
#endif
|
||||
|
||||
//can kit for M5 Atom
|
||||
#ifdef M5_CAN_KIT
|
||||
GWRESOURCE_USE(BASE,M5_CAN_KIT)
|
||||
GWRESOURCE_USE(CAN,M5_CANKIT)
|
||||
#define ESP32_CAN_TX_PIN BOARD_LEFT1
|
||||
#define ESP32_CAN_RX_PIN BOARD_LEFT2
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
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
|
||||
|
||||
unfortunately there is some typo here: M5 uses GROVE for their connections
|
||||
but we have GROOVE here.
|
||||
But to maintain compatibility to older build commands we keep the (wrong) wording
|
||||
*/
|
||||
//M5 Grove stuff
|
||||
#ifndef _GW5MGROVE_H
|
||||
#define _GW5MGROVE_H
|
||||
#ifndef GROOVE_IIC
|
||||
#define GROOVE_IIC(...)
|
||||
#endif
|
||||
#include "GwM5GroveGen.h"
|
||||
|
||||
#if defined(_GWI_IIC1) || defined (_GWI_IIC2)
|
||||
#define _GWIIC
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
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
|
||||
|
||||
unfortunately there is some typo here: M5 uses GROVE for their connections
|
||||
but we have GROOVE here.
|
||||
But to maintain compatibility to older build commands we keep the (wrong) wording
|
||||
|
||||
This file contains M5 grove definitions.
|
||||
They will be expanded to match the supported groves
|
||||
|
||||
Each definition must start with a line that start starts with #GROVE
|
||||
Afterwards you can use normal C header style
|
||||
$GS$ will be replaced with a grove suffix with _ (empty for first)
|
||||
$G$ will be replaced by the simple grove name (empty for base)
|
||||
$Z$ will be replaced by the simple name using "Z" for the first grove
|
||||
*/
|
||||
#GROVE
|
||||
#ifdef SERIAL_GROOVE_485$GS$
|
||||
GWRESOURCE_USE(GROOVE$G$,SERIAL_GROOVE_485$GS$)
|
||||
#define _GWI_SERIAL_GROOVE$GS$ GWSERIAL_TYPE_UNI
|
||||
#endif
|
||||
|
||||
#GROVE
|
||||
#ifdef SERIAL_GROOVE_232$GS$
|
||||
GWRESOURCE_USE(GROOVE$G$,SERIAL_GROOVE_232$GS$)
|
||||
#define _GWI_SERIAL_GROOVE$GS$ GWSERIAL_TYPE_BI
|
||||
#endif
|
||||
|
||||
#GROVE
|
||||
//http://docs.m5stack.com/en/unit/gps
|
||||
#ifdef M5_GPS_UNIT$GS$
|
||||
GWRESOURCE_USE(GROOVE$G$,M5_GPS_UNIT$GS$)
|
||||
#define _GWI_SERIAL_GROOVE$GS$ GWSERIAL_TYPE_RX,9600
|
||||
#endif
|
||||
|
||||
#GROVE
|
||||
//CAN via groove
|
||||
#ifdef M5_CANUNIT$GS$
|
||||
GWRESOURCE_USE(GROOVE$G$,M5_CANUNIT$GS$)
|
||||
GWRESOURCE_USE(CAN,M5_CANUNIT$GS$)
|
||||
#define ESP32_CAN_TX_PIN GROOVE$G$_PIN_2
|
||||
#define ESP32_CAN_RX_PIN GROOVE$G$_PIN_1
|
||||
#endif
|
||||
|
||||
#GROVE
|
||||
#ifdef M5_ENV3$GS$
|
||||
#ifndef M5_GROOVEIIC$GS$
|
||||
#define M5_GROOVEIIC$GS$
|
||||
#endif
|
||||
GROOVE_IIC(SHT3X,$Z$,1)
|
||||
GROOVE_IIC(QMP6988,$Z$,1)
|
||||
#define _GWSHT3X
|
||||
#define _GWQMP6988
|
||||
#endif
|
||||
|
||||
#GROVE
|
||||
//example: -DSHT3XG1_A : defines STH3Xn1 on grove A - x depends on the other devices
|
||||
#ifdef GWSHT3XG1$GS$
|
||||
#ifndef M5_GROOVEIIC$GS$
|
||||
#define M5_GROOVEIIC$GS$
|
||||
#endif
|
||||
GROOVE_IIC(SHT3X,$Z$,1)
|
||||
#define _GWSHT3X
|
||||
#endif
|
||||
|
||||
#GROVE
|
||||
#ifdef GWSHT3XG2$GS$
|
||||
#ifndef M5_GROOVEIIC$GS$
|
||||
#define M5_GROOVEIIC$GS$
|
||||
#endif
|
||||
GROOVE_IIC(SHT3X,$Z$,2)
|
||||
#define _GWSHT3X
|
||||
#endif
|
||||
|
||||
#GROVE
|
||||
#ifdef GWQMP6988G1$GS$
|
||||
#ifndef M5_GROOVEIIC$GS$
|
||||
#define M5_GROOVEIIC$GS$
|
||||
#endif
|
||||
GROOVE_IIC(QMP6988,$Z$,1)
|
||||
#define _GWQMP6988
|
||||
#endif
|
||||
|
||||
#GROVE
|
||||
#ifdef GWQMP6988G2$GS$
|
||||
#ifndef M5_GROOVEIIC$GS$
|
||||
#define M5_GROOVEIIC$GS$
|
||||
#endif
|
||||
GROOVE_IIC(QMP6988,$Z$,2)
|
||||
#define _GWQMP6988
|
||||
#endif
|
||||
|
||||
#GROVE
|
||||
#ifdef GWBME280G1$GS$
|
||||
#ifndef M5_GROOVEIIC$GS$
|
||||
#define M5_GROOVEIIC$GS$
|
||||
#endif
|
||||
GROOVE_IIC(BME280,$Z$,1)
|
||||
#define _GWBME280
|
||||
#endif
|
||||
|
||||
#GROVE
|
||||
#ifdef GWBME280G2$GS$
|
||||
#ifndef M5_GROOVEIIC$GS$
|
||||
#define M5_GROOVEIIC$GS$
|
||||
#endif
|
||||
GROOVE_IIC(BME280,$Z$,2)
|
||||
#define _GWBME280
|
||||
#endif
|
||||
|
||||
#GROVE
|
||||
//select up to 2 IIC devices for grove usage
|
||||
#ifdef M5_GROOVEIIC$GS$
|
||||
GWRESOURCE_USE(GROOVE$G$,M5_GROOVEIIC$GS$)
|
||||
#ifndef _GWI_IIC1
|
||||
__USAGE("IIC1 used by GROVE$GS$")
|
||||
#define _GWI_IIC1 "$Z$",GROOVE$G$_PIN_1,GROOVE$G$_PIN_2
|
||||
#elif ! defined(_GWI_IIC2)
|
||||
__USAGE("IIC2 used by GROVE$GS$")
|
||||
#define _GWI_IIC2 "$Z$",GROOVE$G$_PIN_1,GROOVE$G$_PIN_2
|
||||
#else
|
||||
#error "both iic buses already in use"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#GROVE
|
||||
#ifdef _GWI_SERIAL_GROOVE$GS$
|
||||
#ifndef _GWI_SERIAL1
|
||||
#define _GWI_SERIAL1 GROOVE$G$_PIN_1,GROOVE$G$_PIN_2,_GWI_SERIAL_GROOVE$GS$
|
||||
#elif ! defined(_GWI_SERIAL2)
|
||||
#define _GWI_SERIAL2 GROOVE$G$_PIN_1,GROOVE$G$_PIN_2,_GWI_SERIAL_GROOVE$GS$
|
||||
#else
|
||||
#error "both serial devices already in use"
|
||||
#endif
|
||||
#endif
|
|
@ -2,8 +2,6 @@
|
|||
#ifdef _GWIIC
|
||||
#if defined(GWBME280) || defined(GWBME28011) || defined(GWBME28012)|| defined(GWBME28021)|| defined(GWBME28022)
|
||||
#define _GWBME280
|
||||
#else
|
||||
#undef _GWBME280
|
||||
#endif
|
||||
#else
|
||||
#undef _GWBME280
|
||||
|
@ -17,11 +15,12 @@
|
|||
#include <Adafruit_BME280.h>
|
||||
#endif
|
||||
#ifdef _GWBME280
|
||||
#define PRFX1 "BME28011"
|
||||
#define PRFX2 "BME28012"
|
||||
#define PRFX3 "BME28021"
|
||||
#define PRFX4 "BME28022"
|
||||
class BME280Config : public SensorBase{
|
||||
#define TYPE "BME280"
|
||||
#define PRFX1 TYPE "11"
|
||||
#define PRFX2 TYPE "12"
|
||||
#define PRFX3 TYPE "21"
|
||||
#define PRFX4 TYPE "22"
|
||||
class BME280Config : public IICSensorBase{
|
||||
public:
|
||||
bool prAct=true;
|
||||
bool tmAct=true;
|
||||
|
@ -36,7 +35,7 @@ class BME280Config : public SensorBase{
|
|||
float prOff=0;
|
||||
Adafruit_BME280 *device=nullptr;
|
||||
uint32_t sensorId=-1;
|
||||
BME280Config(GwApi * api, const String &prfx):SensorBase(api,prfx){
|
||||
BME280Config(GwApi * api, const String &prfx):SensorBase(TYPE,api,prfx){
|
||||
}
|
||||
virtual bool isActive(){return prAct||huAct||tmAct;}
|
||||
virtual bool initDevice(GwApi *api,TwoWire *wire){
|
||||
|
@ -106,6 +105,7 @@ class BME280Config : public SensorBase{
|
|||
|
||||
virtual void readConfig(GwConfigHandler *cfg) override
|
||||
{
|
||||
if (ok) return;
|
||||
if (prefix == PRFX1)
|
||||
{
|
||||
busId = 1;
|
||||
|
@ -137,11 +137,13 @@ class BME280Config : public SensorBase{
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
void registerBME280(GwApi *api,SensorList &sensors){
|
||||
static IICSensorBase::Creator creator([](GwApi *api, const String &prfx){
|
||||
return new BME280Config(api,prfx);
|
||||
});
|
||||
IICSensorBase::Creator registerBME280(GwApi *api,IICSensorList &sensors){
|
||||
#if defined(GWBME280) || defined(GWBME28011)
|
||||
{
|
||||
BME280Config *cfg=new BME280Config(api,PRFX1);
|
||||
auto *cfg=creator(api,PRFX1);
|
||||
sensors.add(api,cfg);
|
||||
CHECK_IIC1();
|
||||
#pragma message "GWBME28011 defined"
|
||||
|
@ -149,7 +151,7 @@ void registerBME280(GwApi *api,SensorList &sensors){
|
|||
#endif
|
||||
#if defined(GWBME28012)
|
||||
{
|
||||
BME280Config *cfg=new BME280Config(api,PRFX2);
|
||||
auto *cfg=creator(api,PRFX2);
|
||||
sensors.add(api,cfg);
|
||||
CHECK_IIC1();
|
||||
#pragma message "GWBME28012 defined"
|
||||
|
@ -157,7 +159,7 @@ void registerBME280(GwApi *api,SensorList &sensors){
|
|||
#endif
|
||||
#if defined(GWBME28021)
|
||||
{
|
||||
BME280Config *cfg=new BME280Config(api,PRFX3);
|
||||
auto *cfg=creator(api,PRFX3);
|
||||
sensors.add(api,cfg);
|
||||
CHECK_IIC2();
|
||||
#pragma message "GWBME28021 defined"
|
||||
|
@ -165,15 +167,17 @@ void registerBME280(GwApi *api,SensorList &sensors){
|
|||
#endif
|
||||
#if defined(GWBME28022)
|
||||
{
|
||||
BME280Config *cfg=new BME280Config(api,PRFX4);
|
||||
auto *cfg=creator(api,PRFX4);
|
||||
sensors.add(api,cfg);
|
||||
CHECK_IIC1();
|
||||
#pragma message "GWBME28022 defined"
|
||||
}
|
||||
#endif
|
||||
return creator;
|
||||
}
|
||||
#else
|
||||
void registerBME280(GwApi *api,SensorList &sensors){
|
||||
IICSensorBase::Creator registerBME280(GwApi *api,IICSensorList &sensors){
|
||||
return IICSensorBase::Creator();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef _GWBME280_H
|
||||
#define _GWBME280_H
|
||||
#include "GwIicSensors.h"
|
||||
void registerBME280(GwApi *api,SensorList &sensors);
|
||||
IICSensorBase::Creator registerBME280(GwApi *api,IICSensorList &sensors);
|
||||
#endif
|
|
@ -4,14 +4,17 @@
|
|||
#include "N2kMessages.h"
|
||||
#include "GwXdrTypeMappings.h"
|
||||
#include "GwHardware.h"
|
||||
#include "GwSensor.h"
|
||||
#ifdef _GWIIC
|
||||
#include <Wire.h>
|
||||
#else
|
||||
class TwoWire;
|
||||
#endif
|
||||
|
||||
#define CFG_GET(name,prefix) \
|
||||
cfg->getValue(name, GwConfigDefinitions::prefix ## name)
|
||||
using BusType=TwoWire;
|
||||
using IICSensorList=SensorList<BusType>;
|
||||
using IICSensorBase=SensorBase<BusType>;
|
||||
|
||||
|
||||
template <class CFG>
|
||||
bool addPressureXdr(GwApi *api, CFG &cfg)
|
||||
|
@ -100,35 +103,15 @@ void sendN2kTemperature(GwApi *api,CFG &cfg,double value, int counterId){
|
|||
}
|
||||
|
||||
|
||||
class SensorBase{
|
||||
public:
|
||||
int busId=0;
|
||||
int iid=99; //N2K instanceId
|
||||
int addr=-1;
|
||||
String prefix;
|
||||
long intv=0;
|
||||
bool ok=false;
|
||||
virtual void readConfig(GwConfigHandler *cfg)=0;
|
||||
SensorBase(GwApi *api,const String &prfx):prefix(prfx){
|
||||
}
|
||||
virtual bool isActive(){return false;};
|
||||
virtual bool initDevice(GwApi *api,TwoWire *wire){return false;};
|
||||
virtual bool preinit(GwApi * api){return false;}
|
||||
virtual void measure(GwApi * api,TwoWire *wire, int counterId){};
|
||||
virtual ~SensorBase(){}
|
||||
};
|
||||
|
||||
class SensorList : public std::vector<SensorBase*>{
|
||||
public:
|
||||
void add(GwApi *api, SensorBase *sensor){
|
||||
sensor->readConfig(api->getConfig());
|
||||
api->getLogger()->logDebug(GwLog::LOG,"configured sensor %s, status %d",sensor->prefix.c_str(),(int)sensor->ok);
|
||||
push_back(sensor);
|
||||
}
|
||||
using std::vector<SensorBase*>::vector;
|
||||
};
|
||||
|
||||
#ifndef _GWI_IIC1
|
||||
#define CHECK_IIC1() checkDef(GWIIC_SCL,GWIIC_SDA)
|
||||
#else
|
||||
#define CHECK_IIC1()
|
||||
#endif
|
||||
#ifndef _GWI_IIC2
|
||||
#define CHECK_IIC2() checkDef(GWIIC_SCL2,GWIIC_SDA2)
|
||||
#else
|
||||
#define CHECK_IIC2()
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,4 +1,23 @@
|
|||
#include "GwIicTask.h"
|
||||
class IICGrove
|
||||
{
|
||||
public:
|
||||
String base;
|
||||
String grove;
|
||||
String suffix;
|
||||
IICGrove(const String &b, const String &g, const String &s) : base(b), grove(g), suffix(s) {}
|
||||
String item(const String &grove, const String &bus)
|
||||
{
|
||||
if (grove == this->grove)
|
||||
return base + bus + suffix;
|
||||
return "";
|
||||
}
|
||||
};
|
||||
static std::vector<IICGrove> iicGroveList;
|
||||
#define GROOVE_IIC(base, grove, suffix) \
|
||||
static GwInitializer<IICGrove> base##grove##suffix(iicGroveList, IICGrove(#base, #grove, #suffix));
|
||||
|
||||
|
||||
#include "GwIicSensors.h"
|
||||
#include "GwHardware.h"
|
||||
#include "GwBME280.h"
|
||||
|
@ -6,6 +25,8 @@
|
|||
#include "GwSHT3X.h"
|
||||
#include <map>
|
||||
|
||||
#include "GwTimer.h"
|
||||
|
||||
#ifndef GWIIC_SDA
|
||||
#define GWIIC_SDA -1
|
||||
#endif
|
||||
|
@ -19,14 +40,46 @@
|
|||
#define GWIIC_SCL2 -1
|
||||
#endif
|
||||
|
||||
#include "GwTimer.h"
|
||||
#include "GwHardware.h"
|
||||
|
||||
|
||||
|
||||
void runIicTask(GwApi *api);
|
||||
|
||||
static SensorList sensors;
|
||||
static IICSensorList sensors;
|
||||
static void addGroveItems(std::vector<IICSensorBase::Creator> &creators,GwApi *api, IICSensorList &sensors, const String &bus,const String &grove, int, int)
|
||||
{
|
||||
GwLog *logger=api->getLogger();
|
||||
for (auto &&init : iicGroveList)
|
||||
{
|
||||
LOG_DEBUG(GwLog::DEBUG, "trying grove item %s:%s:%s for grove %s, bus %s",
|
||||
init.base.c_str(),init.grove.c_str(),
|
||||
init.suffix.c_str(),grove.c_str(),bus.c_str()
|
||||
);
|
||||
String prfx = init.item(grove, bus);
|
||||
if (!prfx.isEmpty())
|
||||
{
|
||||
bool found=false;
|
||||
for (auto &&creator : creators)
|
||||
{
|
||||
if (! creator) continue;
|
||||
auto *scfg = creator(api, prfx);
|
||||
scfg->readConfig(api->getConfig());
|
||||
if (scfg->ok)
|
||||
{
|
||||
LOG_DEBUG(GwLog::LOG, "adding %s from grove config", prfx.c_str());
|
||||
sensors.add(api, scfg);
|
||||
found=true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG(GwLog::DEBUG, "unmatched grove sensor config %s for %s", prfx.c_str(), scfg->type.c_str());
|
||||
delete scfg;
|
||||
}
|
||||
}
|
||||
if (! found){
|
||||
LOG_DEBUG(GwLog::ERROR,"no iic sensor found for %s",prfx.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initIicTask(GwApi *api){
|
||||
GwLog *logger=api->getLogger();
|
||||
|
@ -35,14 +88,21 @@ void initIicTask(GwApi *api){
|
|||
#else
|
||||
bool addTask=false;
|
||||
GwConfigHandler *config=api->getConfig();
|
||||
registerSHT3X(api,sensors);
|
||||
registerQMP6988(api,sensors);
|
||||
registerBME280(api,sensors);
|
||||
std::vector<IICSensorBase::Creator> creators;
|
||||
creators.push_back(registerSHT3X(api,sensors));
|
||||
creators.push_back(registerQMP6988(api,sensors));
|
||||
creators.push_back(registerBME280(api,sensors));
|
||||
#ifdef _GWI_IIC1
|
||||
addGroveItems(creators,api,sensors,"1",_GWI_IIC1);
|
||||
#endif
|
||||
#ifdef _GWI_IIC2
|
||||
addGroveItems(creators,api,sensors,"2",_GWI_IIC2);
|
||||
#endif
|
||||
for (auto it=sensors.begin();it != sensors.end();it++){
|
||||
if ((*it)->preinit(api)) addTask=true;
|
||||
}
|
||||
if (addTask){
|
||||
api->addUserTask(runIicTask,"iicTask",3000);
|
||||
api->addUserTask(runIicTask,"iicTask",4000);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -54,6 +114,40 @@ void runIicTask(GwApi *api){
|
|||
return;
|
||||
}
|
||||
#else
|
||||
bool initWireDo(GwLog *logger, TwoWire &wire, int num, const String &dummy, int scl, int sda)
|
||||
{
|
||||
if (sda < 0 || scl < 0)
|
||||
{
|
||||
LOG_DEBUG(GwLog::ERROR, "IIC %d invalid config sda=%d,scl=%d",
|
||||
num, sda, scl);
|
||||
return false;
|
||||
}
|
||||
bool rt = Wire.begin(sda, scl);
|
||||
if (!rt)
|
||||
{
|
||||
LOG_DEBUG(GwLog::ERROR, "unable to initialize IIC %d at sad=%d,scl=%d",
|
||||
num, sda, scl);
|
||||
return rt;
|
||||
}
|
||||
LOG_DEBUG(GwLog::ERROR, "initialized IIC %d at sda=%d,scl=%d",
|
||||
num,sda,scl);
|
||||
return true;
|
||||
}
|
||||
bool initWire(GwLog *logger, TwoWire &wire, int num){
|
||||
if (num == 1){
|
||||
#ifdef _GWI_IIC1
|
||||
return initWireDo(logger,wire,num,_GWI_IIC1);
|
||||
#endif
|
||||
return initWireDo(logger,wire,num,"",GWIIC_SDA,GWIIC_SCL);
|
||||
}
|
||||
if (num == 2){
|
||||
#ifdef _GWI_IIC2
|
||||
return initWireDo(logger,wire,num,_GWI_IIC2);
|
||||
#endif
|
||||
return initWireDo(logger,wire,num,"",GWIIC_SDA2,GWIIC_SCL2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void runIicTask(GwApi *api){
|
||||
GwLog *logger=api->getLogger();
|
||||
std::map<int,TwoWire *> buses;
|
||||
|
@ -66,50 +160,15 @@ void runIicTask(GwApi *api){
|
|||
{
|
||||
case 1:
|
||||
{
|
||||
if (GWIIC_SDA < 0 || GWIIC_SCL < 0)
|
||||
{
|
||||
LOG_DEBUG(GwLog::ERROR, "IIC 1 invalid config sda=%d,scl=%d",
|
||||
(int)GWIIC_SDA, (int)GWIIC_SCL);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool rt = Wire.begin(GWIIC_SDA, GWIIC_SCL);
|
||||
if (!rt)
|
||||
{
|
||||
LOG_DEBUG(GwLog::ERROR, "unable to initialize IIC 1 at sad=%d,scl=%d",
|
||||
(int)GWIIC_SDA, (int)GWIIC_SCL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (initWire(logger,Wire,1)){
|
||||
buses[busId] = &Wire;
|
||||
LOG_DEBUG(GwLog::ERROR, "initialized IIC 1 at sda=%d,scl=%d",
|
||||
(int)GWIIC_SDA, (int)GWIIC_SCL);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
if (GWIIC_SDA2 < 0 || GWIIC_SCL2 < 0)
|
||||
{
|
||||
LOG_DEBUG(GwLog::ERROR, "IIC 2 invalid config sda=%d,scl=%d",
|
||||
(int)GWIIC_SDA2, (int)GWIIC_SCL2);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
bool rt = Wire1.begin(GWIIC_SDA2, GWIIC_SCL2);
|
||||
if (!rt)
|
||||
{
|
||||
LOG_DEBUG(GwLog::ERROR, "unable to initialize IIC 2 at sda=%d,scl=%d",
|
||||
(int)GWIIC_SDA2, (int)GWIIC_SCL2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (initWire(logger,Wire1,2)){
|
||||
buses[busId] = &Wire1;
|
||||
LOG_DEBUG(GwLog::LOG, "initialized IIC 2 at sda=%d,scl=%d",
|
||||
(int)GWIIC_SDA2, (int)GWIIC_SCL2);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -124,7 +183,7 @@ void runIicTask(GwApi *api){
|
|||
GwIntervalRunner timers;
|
||||
int counterId=api->addCounter("iicsensors");
|
||||
for (auto it=sensors.begin();it != sensors.end();it++){
|
||||
SensorBase *cfg=*it;
|
||||
IICSensorBase *cfg=*it;
|
||||
auto bus=buses.find(cfg->busId);
|
||||
if (! cfg->isActive()) continue;
|
||||
if (bus == buses.end()){
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
#define _IIC_GROOVE_LIST
|
||||
#include "GwQMP6988.h"
|
||||
#ifdef _GWQMP6988
|
||||
#define PRFX1 "QMP698811"
|
||||
#define PRFX2 "QMP698812"
|
||||
#define PRFX3 "QMP698821"
|
||||
#define PRFX4 "QMP698822"
|
||||
class QMP6988Config : public SensorBase{
|
||||
#define TYPE "QMP6988"
|
||||
#define PRFX1 TYPE "11"
|
||||
#define PRFX2 TYPE "12"
|
||||
#define PRFX3 TYPE "21"
|
||||
#define PRFX4 TYPE "22"
|
||||
class QMP6988Config : public IICSensorBase{
|
||||
public:
|
||||
String prNam="Pressure";
|
||||
bool prAct=true;
|
||||
tN2kPressureSource prSrc=tN2kPressureSource::N2kps_Atmospheric;
|
||||
float prOff=0;
|
||||
QMP6988 *device=nullptr;
|
||||
QMP6988Config(GwApi* api,const String &prefix):SensorBase(api,prefix){}
|
||||
QMP6988Config(GwApi* api,const String &prefix):SensorBase(TYPE,api,prefix){}
|
||||
virtual bool isActive(){return prAct;};
|
||||
virtual bool initDevice(GwApi *api,TwoWire *wire){
|
||||
if (!isActive()) return false;
|
||||
|
@ -48,6 +50,7 @@ class QMP6988Config : public SensorBase{
|
|||
CFG_GET(prOff,prefix);
|
||||
|
||||
virtual void readConfig(GwConfigHandler *cfg){
|
||||
if (ok) return;
|
||||
if (prefix == PRFX1){
|
||||
busId=1;
|
||||
addr=86;
|
||||
|
@ -76,7 +79,10 @@ class QMP6988Config : public SensorBase{
|
|||
|
||||
}
|
||||
};
|
||||
void registerQMP6988(GwApi *api,SensorList &sensors){
|
||||
static IICSensorBase::Creator creator=[](GwApi *api,const String &prfx){
|
||||
return new QMP6988Config(api,prfx);
|
||||
};
|
||||
IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors){
|
||||
GwLog *logger=api->getLogger();
|
||||
#if defined(GWQMP6988) || defined(GWQMP698811)
|
||||
{
|
||||
|
@ -110,8 +116,11 @@ void registerQMP6988(GwApi *api,SensorList &sensors){
|
|||
#pragma message "GWQMP698822 defined"
|
||||
}
|
||||
#endif
|
||||
return creator;
|
||||
}
|
||||
|
||||
#else
|
||||
void registerQMP6988(GwApi *api,SensorList &sensors){}
|
||||
IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors){
|
||||
return IICSensorBase::Creator();
|
||||
}
|
||||
#endif
|
|
@ -4,8 +4,6 @@
|
|||
#ifdef _GWIIC
|
||||
#if defined(GWQMP6988) || defined(GWQMP698811) || defined(GWQMP698812) || defined(GWQMP698821) || defined(GWQMP698822)
|
||||
#define _GWQMP6988
|
||||
#else
|
||||
#undef _GWQMP6988
|
||||
#endif
|
||||
#else
|
||||
#undef _GWQMP6988
|
||||
|
@ -18,5 +16,5 @@
|
|||
#ifdef _GWQMP6988
|
||||
#include "QMP6988.h"
|
||||
#endif
|
||||
void registerQMP6988(GwApi *api,SensorList &sensors);
|
||||
IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors);
|
||||
#endif
|
|
@ -1,11 +1,12 @@
|
|||
#include "GwSHT3X.h"
|
||||
|
||||
#ifdef _GWSHT3X
|
||||
#define PRFX1 "SHT3X11"
|
||||
#define PRFX2 "SHT3X12"
|
||||
#define PRFX3 "SHT3X21"
|
||||
#define PRFX4 "SHT3X22"
|
||||
class SHT3XConfig : public SensorBase{
|
||||
#define TYPE "SHT3X"
|
||||
#define PRFX1 TYPE "11"
|
||||
#define PRFX2 TYPE "12"
|
||||
#define PRFX3 TYPE "21"
|
||||
#define PRFX4 TYPE "22"
|
||||
|
||||
class SHT3XConfig : public IICSensorBase{
|
||||
public:
|
||||
String tmNam;
|
||||
String huNam;
|
||||
|
@ -15,7 +16,7 @@ class SHT3XConfig : public SensorBase{
|
|||
tN2kTempSource tmSrc;
|
||||
SHT3X *device=nullptr;
|
||||
SHT3XConfig(GwApi *api,const String &prefix):
|
||||
SensorBase(api,prefix){}
|
||||
SensorBase(TYPE,api,prefix){}
|
||||
virtual bool isActive(){
|
||||
return tmAct || huAct;
|
||||
}
|
||||
|
@ -77,6 +78,7 @@ class SHT3XConfig : public SensorBase{
|
|||
CFG_GET(tmSrc,prefix);
|
||||
|
||||
virtual void readConfig(GwConfigHandler *cfg){
|
||||
if (ok) return;
|
||||
if (prefix == PRFX1){
|
||||
busId=1;
|
||||
addr=0x44;
|
||||
|
@ -104,11 +106,14 @@ class SHT3XConfig : public SensorBase{
|
|||
intv*=1000;
|
||||
}
|
||||
};
|
||||
void registerSHT3X(GwApi *api,SensorList &sensors){
|
||||
IICSensorBase::Creator creator=[](GwApi *api,const String &prfx){
|
||||
return new SHT3XConfig(api,prfx);
|
||||
};
|
||||
IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors){
|
||||
GwLog *logger=api->getLogger();
|
||||
#if defined(GWSHT3X) || defined (GWSHT3X11)
|
||||
{
|
||||
SHT3XConfig *scfg=new SHT3XConfig(api,PRFX1);
|
||||
auto *scfg=creator(api,PRFX1);
|
||||
sensors.add(api,scfg);
|
||||
CHECK_IIC1();
|
||||
#pragma message "GWSHT3X11 defined"
|
||||
|
@ -116,7 +121,7 @@ void registerSHT3X(GwApi *api,SensorList &sensors){
|
|||
#endif
|
||||
#if defined(GWSHT3X12)
|
||||
{
|
||||
SHT3XConfig *scfg=new SHT3XConfig(api,PRFX2);
|
||||
auto *scfg=creator(api,PRFX2);
|
||||
sensors.add(api,scfg);
|
||||
CHECK_IIC1();
|
||||
#pragma message "GWSHT3X12 defined"
|
||||
|
@ -124,7 +129,7 @@ void registerSHT3X(GwApi *api,SensorList &sensors){
|
|||
#endif
|
||||
#if defined(GWSHT3X21)
|
||||
{
|
||||
SHT3XConfig *scfg=new SHT3XConfig(api,PRFX3);
|
||||
auto *scfg=creator(api,PRFX3);
|
||||
sensors.add(api,scfg);
|
||||
CHECK_IIC2();
|
||||
#pragma message "GWSHT3X21 defined"
|
||||
|
@ -132,16 +137,18 @@ void registerSHT3X(GwApi *api,SensorList &sensors){
|
|||
#endif
|
||||
#if defined(GWSHT3X22)
|
||||
{
|
||||
SHT3XConfig *scfg=new SHT3XConfig(api,PRFX4);
|
||||
auto *scfg=creator(api,PRFX4);
|
||||
sensors.add(api,scfg);
|
||||
CHECK_IIC2();
|
||||
#pragma message "GWSHT3X22 defined"
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
void registerSHT3X(GwApi *api,SensorList &sensors){
|
||||
return creator;
|
||||
};
|
||||
|
||||
#else
|
||||
IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors){
|
||||
return IICSensorBase::Creator();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
#ifdef _GWIIC
|
||||
#if defined(GWSHT3X) || defined(GWSHT3X11) || defined(GWSHT3X12) || defined(GWSHT3X21) || defined(GWSHT3X22)
|
||||
#define _GWSHT3X
|
||||
#else
|
||||
#undef _GWSHT3X
|
||||
#endif
|
||||
#else
|
||||
#undef _GWSHT3X
|
||||
|
@ -18,5 +16,5 @@
|
|||
#ifdef _GWSHT3X
|
||||
#include "SHT3X.h"
|
||||
#endif
|
||||
void registerSHT3X(GwApi *api,SensorList &sensors);
|
||||
IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors);
|
||||
#endif
|
|
@ -29,7 +29,6 @@ private:
|
|||
MyAisDecoder *aisDecoder=NULL;
|
||||
ConverterList<NMEA0183DataToN2KFunctions, SNMEA0183Msg> converters;
|
||||
std::map<String,unsigned long> lastSends;
|
||||
unsigned long minSendInterval=50;
|
||||
GwXDRMappings *xdrMappings;
|
||||
class WaypointNumber{
|
||||
public:
|
||||
|
@ -92,7 +91,7 @@ private:
|
|||
return false;
|
||||
}
|
||||
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){
|
||||
if (v != NMEA0183DoubleNA){
|
||||
|
@ -304,7 +303,7 @@ private:
|
|||
LOG_DEBUG(GwLog::DEBUG + 1, "convert RMB");
|
||||
tRMB 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;
|
||||
}
|
||||
tN2kMsg n2kMsg;
|
||||
|
@ -359,6 +358,7 @@ private:
|
|||
LOG_DEBUG(GwLog::DEBUG, "invalid status %c for RMC %s",status, msg.line);
|
||||
return;
|
||||
}
|
||||
lastRmc=millis(); //we received an RMC that is not from us
|
||||
tN2kMsg n2kMsg;
|
||||
if (
|
||||
UD(GPST) &&
|
||||
|
@ -666,21 +666,33 @@ private:
|
|||
void convertDBT(const SNMEA0183Msg &msg){
|
||||
return convertDBKx(msg,DBT);
|
||||
}
|
||||
|
||||
#define validInstance(name) (name >= 0 && name <= 253)
|
||||
void convertRSA(const SNMEA0183Msg &msg){
|
||||
double RPOS=NMEA0183DoubleNA;
|
||||
double PRPOS=NMEA0183DoubleNA;
|
||||
if (msg.FieldCount() < 4)
|
||||
{
|
||||
LOG_DEBUG(GwLog::DEBUG, "failed to parse RSA %s", msg.line);
|
||||
return;
|
||||
}
|
||||
if (msg.FieldLen(0)>0){
|
||||
if (msg.Field(1)[0] != 'A') return;
|
||||
RPOS=degToRad*atof(msg.Field(0));
|
||||
tN2kMsg n2kMsg;
|
||||
if (! UD(RPOS)) return;
|
||||
SetN2kRudder(n2kMsg,RPOS);
|
||||
send(n2kMsg,msg.sourceId);
|
||||
if (msg.FieldLen(0)>0){
|
||||
if (msg.Field(1)[0] == 'A'){
|
||||
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,
|
||||
GwXDRMappings *xdrMappings,
|
||||
unsigned long minSendInterval)
|
||||
const GwConverterConfig &cfg)
|
||||
: NMEA0183DataToN2K(logger, boatData, callback)
|
||||
{
|
||||
this->minSendInterval=minSendInterval;
|
||||
this->config=cfg;
|
||||
this->xdrMappings=xdrMappings;
|
||||
aisDecoder= new MyAisDecoder(logger,this->sender);
|
||||
registerConverters();
|
||||
|
@ -1074,7 +1086,7 @@ public:
|
|||
|
||||
NMEA0183DataToN2K* NMEA0183DataToN2K::create(GwLog *logger,GwBoatData *boatData,N2kSender callback,
|
||||
GwXDRMappings *xdrMappings,
|
||||
unsigned long minSendInterval){
|
||||
return new NMEA0183DataToN2KFunctions(logger, boatData,callback,xdrMappings,minSendInterval);
|
||||
const GwConverterConfig &config){
|
||||
return new NMEA0183DataToN2KFunctions(logger, boatData,callback,xdrMappings,config);
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "GwBoatData.h"
|
||||
#include "N2kMessages.h"
|
||||
#include "GwXDRMappings.h"
|
||||
#include "GwConverterConfig.h"
|
||||
|
||||
class NMEA0183DataToN2K{
|
||||
public:
|
||||
|
@ -12,14 +13,17 @@ class NMEA0183DataToN2K{
|
|||
GwLog * logger;
|
||||
GwBoatData *boatData;
|
||||
N2kSender sender;
|
||||
GwConverterConfig config;
|
||||
unsigned long lastRmc=millis();
|
||||
public:
|
||||
NMEA0183DataToN2K(GwLog *logger,GwBoatData *boatData,N2kSender callback);
|
||||
virtual bool parseAndSend(const char *buffer, int sourceId)=0;
|
||||
virtual unsigned long *handledPgns()=0;
|
||||
virtual int numConverters()=0;
|
||||
virtual String handledKeys()=0;
|
||||
unsigned long getLastRmc()const {return lastRmc; }
|
||||
static NMEA0183DataToN2K* create(GwLog *logger,GwBoatData *boatData,N2kSender callback,
|
||||
GwXDRMappings *xdrMappings,
|
||||
unsigned long minSendInterval);
|
||||
const GwConverterConfig &config);
|
||||
};
|
||||
#endif
|
|
@ -43,7 +43,7 @@ N2kDataToNMEA0183::N2kDataToNMEA0183(GwLog * logger, GwBoatData *boatData,
|
|||
|
||||
|
||||
//*****************************************************************************
|
||||
void N2kDataToNMEA0183::loop() {
|
||||
void N2kDataToNMEA0183::loop(unsigned long) {
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
|
@ -62,20 +62,19 @@ class N2kToNMEA0183Functions : public N2kDataToNMEA0183
|
|||
{
|
||||
|
||||
private:
|
||||
int minXdrInterval=100; //minimal interval between 2 sends of the same transducer
|
||||
GwXDRMappings *xdrMappings;
|
||||
ConverterList<N2kToNMEA0183Functions,tN2kMsg> converters;
|
||||
std::map<String,unsigned long> lastSendTransducers;
|
||||
static const unsigned long RMCPeriod = 500;
|
||||
tNMEA0183Msg xdrMessage;
|
||||
bool xdrOpened=false;
|
||||
int xdrCount=0;
|
||||
unsigned long lastRmcSent=0;
|
||||
|
||||
bool addToXdr(GwXDRFoundMapping::XdrEntry entry){
|
||||
auto it=lastSendTransducers.find(entry.transducer);
|
||||
unsigned long now=millis();
|
||||
if (it != lastSendTransducers.end()){
|
||||
if ((it->second + minXdrInterval) > now) return false;
|
||||
if ((it->second + config.minXdrInterval) > now) return false;
|
||||
}
|
||||
lastSendTransducers[entry.transducer]=now;
|
||||
if (! xdrOpened){
|
||||
|
@ -134,9 +133,6 @@ private:
|
|||
return boatData->update((double)value,sourceId,mapping);
|
||||
}
|
||||
|
||||
unsigned long LastPosSend;
|
||||
unsigned long NextRMCSend;
|
||||
unsigned long lastLoopTime;
|
||||
|
||||
virtual unsigned long *handledPgns()
|
||||
{
|
||||
|
@ -166,7 +162,6 @@ private:
|
|||
{
|
||||
return converters.numConverters();
|
||||
}
|
||||
void SetNextRMCSend() { NextRMCSend = millis() + RMCPeriod; }
|
||||
|
||||
//*************** the converters ***********************
|
||||
void HandleHeading(const tN2kMsg &N2kMsg)
|
||||
|
@ -546,11 +541,9 @@ private:
|
|||
void SendRMC()
|
||||
{
|
||||
long now = millis();
|
||||
if (NextRMCSend <= millis() &&
|
||||
boatData->LAT->isValid(now) &&
|
||||
boatData->LAT->getLastSource() == sourceId
|
||||
)
|
||||
if (boatData->LAT->isValid(now) && boatData->LON->isValid(now))
|
||||
{
|
||||
lastRmcSent=now;
|
||||
tNMEA0183Msg NMEA0183Msg;
|
||||
if (NMEA0183SetRMC(NMEA0183Msg,
|
||||
|
||||
|
@ -565,7 +558,6 @@ private:
|
|||
{
|
||||
SendMessage(NMEA0183Msg);
|
||||
}
|
||||
SetNextRMCSend();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -611,24 +603,38 @@ private:
|
|||
|
||||
if (ParseN2kRudder(N2kMsg, RudderPosition, Instance, RudderDirectionOrder, AngleOrder))
|
||||
{
|
||||
|
||||
if (Instance == config.starboardRudderInstance){
|
||||
updateDouble(boatData->RPOS, RudderPosition);
|
||||
if (Instance != 0)
|
||||
}
|
||||
else if (Instance == config.portRudderInstance){
|
||||
updateDouble(boatData->PRPOS, RudderPosition);
|
||||
}
|
||||
else{
|
||||
return;
|
||||
}
|
||||
|
||||
tNMEA0183Msg NMEA0183Msg;
|
||||
|
||||
if (!NMEA0183Msg.Init("RSA", talkerId))
|
||||
return;
|
||||
if (!NMEA0183Msg.AddDoubleField(formatCourse(RudderPosition)))
|
||||
return;
|
||||
if (!NMEA0183Msg.AddStrField("A"))
|
||||
return;
|
||||
if (!NMEA0183Msg.AddDoubleField(0.0))
|
||||
return;
|
||||
if (!NMEA0183Msg.AddStrField("A"))
|
||||
return;
|
||||
|
||||
auto rpos=boatData->RPOS;
|
||||
if (rpos->isValid()){
|
||||
if (!NMEA0183Msg.AddDoubleField(formatWind(rpos->getData())))return;
|
||||
if (!NMEA0183Msg.AddStrField("A"))return;
|
||||
}
|
||||
else{
|
||||
if (!NMEA0183Msg.AddDoubleField(0.0))return;
|
||||
if (!NMEA0183Msg.AddStrField("V"))return;
|
||||
}
|
||||
auto prpos=boatData->PRPOS;
|
||||
if (prpos->isValid()){
|
||||
if (!NMEA0183Msg.AddDoubleField(formatWind(prpos->getData())))return;
|
||||
if (!NMEA0183Msg.AddStrField("A"))return;
|
||||
}
|
||||
else{
|
||||
if (!NMEA0183Msg.AddDoubleField(0.0))return;
|
||||
if (!NMEA0183Msg.AddStrField("V"))return;
|
||||
}
|
||||
SendMessage(NMEA0183Msg);
|
||||
}
|
||||
}
|
||||
|
@ -1508,35 +1514,30 @@ private:
|
|||
public:
|
||||
N2kToNMEA0183Functions(GwLog *logger, GwBoatData *boatData,
|
||||
SendNMEA0183MessageCallback callback,
|
||||
String talkerId, GwXDRMappings *xdrMappings, int minXdrInterval)
|
||||
String talkerId, GwXDRMappings *xdrMappings, const GwConverterConfig &cfg)
|
||||
: N2kDataToNMEA0183(logger, boatData, callback,talkerId)
|
||||
{
|
||||
LastPosSend = 0;
|
||||
lastLoopTime = 0;
|
||||
NextRMCSend = millis() + RMCPeriod;
|
||||
|
||||
this->logger = logger;
|
||||
this->boatData = boatData;
|
||||
this->xdrMappings=xdrMappings;
|
||||
this->minXdrInterval=minXdrInterval;
|
||||
this->config=cfg;
|
||||
registerConverters();
|
||||
}
|
||||
virtual void loop()
|
||||
virtual void loop(unsigned long lastExtRmc) override
|
||||
{
|
||||
N2kDataToNMEA0183::loop();
|
||||
N2kDataToNMEA0183::loop(lastExtRmc);
|
||||
unsigned long now = millis();
|
||||
if (now < (lastLoopTime + 100))
|
||||
return;
|
||||
lastLoopTime = now;
|
||||
if (config.rmcInterval > 0 && (lastExtRmc + config.rmcCheckTime) <= now && (lastRmcSent + config.rmcInterval) <= now){
|
||||
SendRMC();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
N2kDataToNMEA0183* N2kDataToNMEA0183::create(GwLog *logger, GwBoatData *boatData,
|
||||
SendNMEA0183MessageCallback callback, String talkerId, GwXDRMappings *xdrMappings,
|
||||
int minXdrInterval){
|
||||
const GwConverterConfig &cfg){
|
||||
LOG_DEBUG(GwLog::LOG,"creating N2kToNMEA0183");
|
||||
return new N2kToNMEA0183Functions(logger,boatData,callback, talkerId,xdrMappings,minXdrInterval);
|
||||
return new N2kToNMEA0183Functions(logger,boatData,callback, talkerId,xdrMappings,cfg);
|
||||
}
|
||||
//*****************************************************************************
|
||||
|
|
|
@ -26,9 +26,10 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
#include <NMEA0183.h>
|
||||
#include <NMEA2000.h>
|
||||
|
||||
#include <GwLog.h>
|
||||
#include <GwBoatData.h>
|
||||
#include <GwXDRMappings.h>
|
||||
#include "GwLog.h"
|
||||
#include "GwBoatData.h"
|
||||
#include "GwXDRMappings.h"
|
||||
#include "GwConverterConfig.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
class GwJsonDocument;
|
||||
|
@ -36,8 +37,8 @@ class N2kDataToNMEA0183
|
|||
{
|
||||
public:
|
||||
typedef std::function<void(const tNMEA0183Msg &NMEA0183Msg,int id)> SendNMEA0183MessageCallback;
|
||||
|
||||
protected:
|
||||
GwConverterConfig config;
|
||||
GwLog *logger;
|
||||
GwBoatData *boatData;
|
||||
int sourceId=0;
|
||||
|
@ -49,9 +50,9 @@ protected:
|
|||
|
||||
public:
|
||||
static N2kDataToNMEA0183* create(GwLog *logger, GwBoatData *boatData, SendNMEA0183MessageCallback callback,
|
||||
String talkerId, GwXDRMappings *xdrMappings,int minXdrInterval=100);
|
||||
String talkerId, GwXDRMappings *xdrMappings,const GwConverterConfig &cfg);
|
||||
virtual void HandleMsg(const tN2kMsg &N2kMsg, int sourceId) = 0;
|
||||
virtual void loop();
|
||||
virtual void loop(unsigned long lastRmc);
|
||||
virtual ~N2kDataToNMEA0183(){}
|
||||
virtual unsigned long* handledPgns()=0;
|
||||
virtual int numPgns()=0;
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
(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 _GWSENSORS_H
|
||||
#define _GWSENSORS_H
|
||||
#include "GwApi.h"
|
||||
#include "GwLog.h"
|
||||
template<typename BUS>
|
||||
class SensorBase{
|
||||
public:
|
||||
int busId=0;
|
||||
int iid=99; //N2K instanceId
|
||||
int addr=-1;
|
||||
const String prefix;
|
||||
const String type;
|
||||
long intv=0;
|
||||
bool ok=false;
|
||||
virtual void readConfig(GwConfigHandler *cfg)=0;
|
||||
SensorBase(const String &tn,GwApi *api,const String &prfx):type(tn),prefix(prfx){
|
||||
}
|
||||
using Creator=std::function<SensorBase<BUS> *(GwApi *api,const String &prfx)>;
|
||||
virtual bool isActive(){return false;};
|
||||
virtual bool initDevice(GwApi *api,BUS *wire){return false;};
|
||||
virtual bool preinit(GwApi * api){return false;}
|
||||
virtual void measure(GwApi * api,BUS *wire, int counterId){};
|
||||
virtual ~SensorBase(){}
|
||||
};
|
||||
|
||||
template<typename BUS>
|
||||
class SensorList : public std::vector<SensorBase<BUS>*>{
|
||||
public:
|
||||
void add(GwApi *api, SensorBase<BUS> *sensor){
|
||||
sensor->readConfig(api->getConfig());
|
||||
api->getLogger()->logDebug(GwLog::LOG,"configured sensor %s, status %d",sensor->prefix.c_str(),(int)sensor->ok);
|
||||
this->push_back(sensor);
|
||||
}
|
||||
using std::vector<SensorBase<BUS>*>::vector;
|
||||
};
|
||||
|
||||
|
||||
#define CFG_GET(name,prefix) \
|
||||
cfg->getValue(name, GwConfigDefinitions::prefix ## name)
|
||||
|
||||
#endif
|
|
@ -115,11 +115,21 @@ void GwSerial::readMessages(GwMessageFetcher *writer){
|
|||
writer->handleBuffer(readBuffer);
|
||||
}
|
||||
|
||||
void GwSerial::flush(){
|
||||
if (! isInitialized()) return;
|
||||
while (write() == GwBuffer::AGAIN){
|
||||
bool GwSerial::flush(long max){
|
||||
if (! isInitialized()) return false;
|
||||
if (! availableWrite) {
|
||||
if ( serial->availableForWrite() < 1){
|
||||
return false;
|
||||
}
|
||||
availableWrite=true;
|
||||
}
|
||||
auto start=millis();
|
||||
while (millis() < (start+max)){
|
||||
if (write() != GwBuffer::AGAIN) return true;
|
||||
vTaskDelay(1);
|
||||
}
|
||||
availableWrite=(serial->availableForWrite() > 0);
|
||||
return false;
|
||||
}
|
||||
Stream * GwSerial::getStream(bool partialWrite){
|
||||
return new GwSerialStream(this,partialWrite);
|
||||
|
|
|
@ -17,6 +17,7 @@ class GwSerial : public GwChannelInterface{
|
|||
int overflows=0;
|
||||
size_t enqueue(const uint8_t *data, size_t len,bool partial=false);
|
||||
Stream *serial;
|
||||
bool availableWrite=false; //if this is false we will wait for availabkleWrite until we flush again
|
||||
public:
|
||||
static const int bufferSize=200;
|
||||
GwSerial(GwLog *logger,Stream *stream,int id,bool allowRead=true);
|
||||
|
@ -25,8 +26,9 @@ class GwSerial : public GwChannelInterface{
|
|||
virtual size_t sendToClients(const char *buf,int sourceId,bool partial=false);
|
||||
virtual void loop(bool handleRead=true,bool handleWrite=true);
|
||||
virtual void readMessages(GwMessageFetcher *writer);
|
||||
void flush();
|
||||
bool flush(long millis=200);
|
||||
virtual Stream *getStream(bool partialWrites);
|
||||
bool getAvailableWrite(){return availableWrite;}
|
||||
friend GwSerialStream;
|
||||
};
|
||||
#endif
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
(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
|
||||
*/
|
||||
#include "GWDMS22B.h"
|
||||
#include "GwApi.h"
|
||||
#include "N2kMessages.h"
|
||||
#include "GwHardware.h"
|
||||
|
||||
|
||||
#define CHECK_BUS(BUS) \
|
||||
checkDef("missing config for " #BUS,GW ## BUS ## _CLK ,GW ## BUS ## _MISO);
|
||||
|
||||
#define ADD22B(PRFX,BUS) \
|
||||
{\
|
||||
CHECK_BUS(BUS); \
|
||||
GWDMS22B *dms=new GWDMS22B(api,#PRFX,GW ## BUS ## _HOST);\
|
||||
sensors.add(api,dms); \
|
||||
}
|
||||
|
||||
#ifdef GWDMS22B11
|
||||
#define ADD22B11 ADD22B(DMS22B11,SPI1)
|
||||
#ifndef GWDMS22B11_CS
|
||||
#define GWDMS22B11_CS -1
|
||||
#endif
|
||||
#else
|
||||
#define GWDMS22B11_CS -1
|
||||
#define ADD22B11
|
||||
#endif
|
||||
|
||||
#ifdef GWDMS22B12
|
||||
#define ADD22B12 ADD22B(DMS22B12,SPI1)
|
||||
#ifndef GWDMS22B12_CS
|
||||
#error "you need to define GWDMS22B12_CS"
|
||||
#endif
|
||||
#if GWDMS22B11_CS == -1
|
||||
#error "multiple devices on one SPI bus need chip select defines - GWDMS22B11_CS is unset"
|
||||
#endif
|
||||
#else
|
||||
#define GWDMS22B12_CS -1
|
||||
#define ADD22B12
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef GWDMS22B21
|
||||
#define ADD22B21 ADD22B(DMS22B21,SPI2)
|
||||
#ifndef GWDMS22B21_CS
|
||||
#define GWDMS22B21_CS -1
|
||||
#endif
|
||||
#else
|
||||
#define GWDMS22B21_CS -1
|
||||
#define ADD22B21
|
||||
#endif
|
||||
|
||||
#ifdef GWDMS22B22
|
||||
#define ADD22B22 ADD22B(DMS22B22,SPI2)
|
||||
#ifndef GWDMS22B22_CS
|
||||
#error "you need to define GWDMS22B22_CS"
|
||||
#endif
|
||||
#if GWDMS22B21_CS == -1
|
||||
#error "multiple devices on one SPI bus need chip select defines - GWDMS22B21_CS is unset"
|
||||
#endif
|
||||
#else
|
||||
#define GWDMS22B22_CS -1
|
||||
#define ADD22B22
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
class GWDMS22B : public SSISensor{
|
||||
int zero=2047;
|
||||
bool invt=false;
|
||||
String zeroConfigName;
|
||||
public:
|
||||
GWDMS22B(GwApi *api,const String &prfx, int host):SSISensor("DMS22B",api,prfx,host){}
|
||||
virtual bool preinit(GwApi * api){
|
||||
GwLog *logger=api->getLogger();
|
||||
LOG_DEBUG(GwLog::LOG,"DMS22B configured, prefix=%s, intv=%f, active=%d",prefix.c_str(),fintv,(int)act);
|
||||
api->addCapability(prefix,"true");
|
||||
return act;
|
||||
}
|
||||
virtual void measure(GwApi * api,BusType *bus, int counterId){
|
||||
GwLog *logger=api->getLogger();
|
||||
uint32_t value=0;
|
||||
esp_err_t res=readData(value);
|
||||
if (res != ESP_OK){
|
||||
LOG_DEBUG(GwLog::ERROR,"unable to measure %s: %d",prefix.c_str(),(int)res);
|
||||
}
|
||||
double resolved=(((int)value-zero)*360.0/mask);
|
||||
if (invt) resolved=-resolved;
|
||||
LOG_DEBUG(GwLog::DEBUG,"measure %s : %d, resolved: %f",prefix.c_str(),value,(float)resolved);
|
||||
tN2kMsg msg;
|
||||
SetN2kRudder(msg,DegToRad(resolved),iid);
|
||||
api->sendN2kMessage(msg);
|
||||
api->increment(counterId,prefix);
|
||||
api->setCalibrationValue(zeroConfigName,(double)value);
|
||||
}
|
||||
#define DMS22B(PRFX,...) \
|
||||
if (prefix == #PRFX) {\
|
||||
CFG_GET(act,PRFX); \
|
||||
CFG_GET(iid,PRFX); \
|
||||
CFG_GET(fintv,PRFX); \
|
||||
CFG_GET(zero,PRFX); \
|
||||
zeroConfigName=GwConfigDefinitions::PRFX ## zero;\
|
||||
CFG_GET(invt,PRFX); \
|
||||
bits=12; \
|
||||
clock=500000; \
|
||||
cs=GW ## PRFX ## _CS; \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
|
||||
|
||||
virtual void readConfig(GwConfigHandler *cfg){
|
||||
DMS22B(DMS22B11);
|
||||
DMS22B(DMS22B12);
|
||||
DMS22B(DMS22B21);
|
||||
DMS22B(DMS22B22);
|
||||
intv=1000*fintv;
|
||||
}
|
||||
};
|
||||
|
||||
void registerDMS22B(GwApi *api,SpiSensorList &sensors){
|
||||
ADD22B11
|
||||
ADD22B12
|
||||
ADD22B21
|
||||
ADD22B22
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
(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
|
||||
*/
|
||||
/*
|
||||
SSI sensor DMS22B - https://www.mouser.de/datasheet/2/54/bour_s_a0011704065_1-2262614.pdf
|
||||
*/
|
||||
#ifndef _GWDMS22B_H
|
||||
#define _GWDMS22B_H
|
||||
#include "GwSpiSensor.h"
|
||||
void registerDMS22B(GwApi *api,SpiSensorList &sensors);
|
||||
#endif
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
(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
|
||||
*/
|
||||
//for SSI refer to https://www.posital.com/media/posital_media/documents/AbsoluteEncoders_Context_Technology_SSI_AppNote.pdf
|
||||
#ifndef _GWSPISENSOR_H
|
||||
#define _GWSPISENSOR_H
|
||||
#include <driver/spi_master.h>
|
||||
#include "GwSensor.h"
|
||||
#include <memory>
|
||||
|
||||
class SPIBus{
|
||||
spi_host_device_t hd;
|
||||
bool initialized=false;
|
||||
public:
|
||||
SPIBus(spi_host_device_t h):hd(h){}
|
||||
bool init(GwLog*logger,int mosi=-1,int miso=-1,int clck=-1){
|
||||
spi_bus_config_t buscfg = {
|
||||
.mosi_io_num = mosi,
|
||||
.miso_io_num = miso,
|
||||
.sclk_io_num = clck,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = 0
|
||||
};
|
||||
esp_err_t res=spi_bus_initialize(hd,&buscfg,0);
|
||||
if (res == ESP_OK){
|
||||
LOG_DEBUG(GwLog::LOG,"initialzed SPI bus %d,mosi=%d,miso=%d,clock=%d",
|
||||
(int)hd,mosi,miso,clck);
|
||||
initialized=true;
|
||||
}
|
||||
else{
|
||||
LOG_DEBUG(GwLog::ERROR,"unable to initialize SPI bus %d,mosi=%d,miso=%d,clock=%d, error=%d",
|
||||
(int)hd,mosi,miso,clck,(int)res);
|
||||
}
|
||||
return initialized;
|
||||
}
|
||||
spi_host_device_t host() const { return hd;}
|
||||
};
|
||||
|
||||
using BusType=SPIBus;
|
||||
|
||||
class SSIDevice{
|
||||
spi_device_handle_t spi;
|
||||
spi_host_device_t host;
|
||||
bool initialized=false;
|
||||
int bits=12;
|
||||
public:
|
||||
SSIDevice(const SPIBus *bus):host(bus->host()){}
|
||||
bool init(GwLog*logger,int clock,int cs=-1,int bits=12){
|
||||
this->bits=bits;
|
||||
spi_device_interface_config_t devcfg = {
|
||||
.command_bits = 0,
|
||||
.address_bits = 0,
|
||||
.dummy_bits = 0,
|
||||
.mode = 2,
|
||||
.duty_cycle_pos = 128,
|
||||
.cs_ena_pretrans = 0,
|
||||
.cs_ena_posttrans =0,
|
||||
.clock_speed_hz = clock,
|
||||
.input_delay_ns =0,
|
||||
.spics_io_num = cs, //CS pin
|
||||
.queue_size = 1 //see https://github.com/espressif/esp-idf/issues/9450
|
||||
};
|
||||
esp_err_t res=spi_bus_add_device(host,&devcfg,&spi);
|
||||
if (res == ESP_OK){
|
||||
LOG_DEBUG(GwLog::LOG,"added SSI device to bus %d, cs=%d, clock=%d",
|
||||
(int)host,cs,clock);
|
||||
initialized=true;
|
||||
}
|
||||
else{
|
||||
LOG_DEBUG(GwLog::ERROR,"unable to add SSI device to bus %d, cs=%d, clock=%d, error=%d",
|
||||
(int)host,cs,clock,(int) res);
|
||||
}
|
||||
return initialized;
|
||||
}
|
||||
bool isInitialized() const { return initialized;}
|
||||
spi_device_handle_t & device(){return spi;}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class SSISensor : public SensorBase<BusType>{
|
||||
std::unique_ptr<SSIDevice> device;
|
||||
protected:
|
||||
int bits=12;
|
||||
int mask=0xffff;
|
||||
int cs=-1;
|
||||
int clock=0;
|
||||
bool act=false;
|
||||
float fintv=0;
|
||||
virtual bool initSSI(GwLog*logger,const SPIBus *bus,
|
||||
int clock,int cs, int bits){
|
||||
mask= (1 << bits)-1;
|
||||
device.reset(new SSIDevice(bus));
|
||||
return device->init(logger,clock,cs,bits);
|
||||
}
|
||||
esp_err_t readData(uint32_t &res)
|
||||
{
|
||||
struct spi_transaction_t ta = {
|
||||
.flags = SPI_TRANS_USE_RXDATA,
|
||||
.cmd = 0,
|
||||
.addr = 0,
|
||||
.length = bits+1,
|
||||
.rxlength = 0};
|
||||
esp_err_t ret = spi_device_queue_trans(device->device(), &ta, portMAX_DELAY);
|
||||
if (ret != ESP_OK) return ret;
|
||||
struct spi_transaction_t *rs = NULL;
|
||||
ret = spi_device_get_trans_result(device->device(), &rs, portMAX_DELAY);
|
||||
if (ret != ESP_OK) return ret;
|
||||
if (rs == NULL) return ESP_ERR_INVALID_RESPONSE;
|
||||
res=SPI_SWAP_DATA_RX(*(uint32_t*)rs->rx_data,bits+1);
|
||||
res&=mask;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
public:
|
||||
SSISensor(const String &type,GwApi *api,const String &prfx, int host):SensorBase(type,api,prfx)
|
||||
{
|
||||
busId=host;
|
||||
}
|
||||
virtual bool isActive(){return act;};
|
||||
virtual bool initDevice(GwApi *api,BusType *bus){
|
||||
return initSSI(api->getLogger(),bus, clock,cs,bits);
|
||||
};
|
||||
|
||||
};
|
||||
using SpiSensorList=SensorList<BusType>;
|
||||
#define GWSPI1_HOST SPI2_HOST
|
||||
#define GWSPI2_HOST SPI3_HOST
|
||||
#endif
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
(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
|
||||
*/
|
||||
#include "GwSpiTask.h"
|
||||
#include "GwSpiSensor.h"
|
||||
#include "GWDMS22B.h"
|
||||
#include "GwTimer.h"
|
||||
#include "GwHardware.h"
|
||||
|
||||
static SPIBus bus1(GWSPI1_HOST);
|
||||
static SPIBus bus2(GWSPI2_HOST);
|
||||
|
||||
static SpiSensorList sensors;
|
||||
|
||||
#ifdef GWSPI1_CLK
|
||||
static const int spi1clk=GWSPI1_CLK;
|
||||
#else
|
||||
static const int spi1clk=-1;
|
||||
#endif
|
||||
#ifdef GWSPI1_MISO
|
||||
static const int spi1miso=GWSPI1_MISO;
|
||||
#else
|
||||
static const int spi1miso=-1;
|
||||
#endif
|
||||
#ifdef GWSPI1_MOSI
|
||||
static const int spi1mosi=GWSPI1_MOSI;
|
||||
#else
|
||||
static const int spi1mosi=-1;
|
||||
#endif
|
||||
|
||||
#ifdef GWSPI2_CLK
|
||||
static const int spi2clk=GWSPI2_CLK;
|
||||
#else
|
||||
static const int spi2clk=-1;
|
||||
#endif
|
||||
#ifdef GWSPI2_MISO
|
||||
static const int spi2miso=GWSPI2_MISO;
|
||||
#else
|
||||
static const int spi2miso=-1;
|
||||
#endif
|
||||
#ifdef GWSPI2_MOSI
|
||||
static const int spi2mosi=GWSPI2_MOSI;
|
||||
#else
|
||||
static const int spi2mosi=-1;
|
||||
#endif
|
||||
|
||||
void runSpiTask(GwApi *api){
|
||||
GwLog *logger=api->getLogger();
|
||||
std::map<int,SPIBus *> buses;
|
||||
for (auto && sensor:sensors){
|
||||
int busId=sensor->busId;
|
||||
auto bus=buses.find(busId);
|
||||
if (bus == buses.end()){
|
||||
switch (busId)
|
||||
{
|
||||
case GWSPI1_HOST:
|
||||
if (spi1clk < 0){
|
||||
LOG_DEBUG(GwLog::ERROR,"SPI bus 1 not configured, cannot create %s",sensor->prefix.c_str());
|
||||
}
|
||||
else{
|
||||
if (bus1.init(logger,spi1mosi,spi1miso,spi1clk)){
|
||||
buses[busId]=&bus1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GWSPI2_HOST:
|
||||
if (spi2clk < 0){
|
||||
LOG_DEBUG(GwLog::ERROR,"SPI bus 2 not configured, cannot create %s",sensor->prefix.c_str());
|
||||
}
|
||||
else{
|
||||
if (bus2.init(logger,spi2mosi,spi2miso,spi2clk)){
|
||||
buses[busId]=&bus2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_DEBUG(GwLog::ERROR,"invalid busid %d for %s",busId,sensor->prefix.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
GwConfigHandler *config=api->getConfig();
|
||||
bool runLoop=false;
|
||||
GwIntervalRunner timers;
|
||||
int counterId=api->addCounter("spisensors");
|
||||
for (auto && sensor:sensors){
|
||||
if (!sensor->isActive()) continue;
|
||||
auto bus=buses.find(sensor->busId);
|
||||
if (bus == buses.end()){
|
||||
continue;
|
||||
}
|
||||
bool rt=sensor->initDevice(api,bus->second);
|
||||
if (rt){
|
||||
runLoop=true;
|
||||
timers.addAction(sensor->intv,[bus,api,sensor,counterId](){
|
||||
sensor->measure(api,bus->second,counterId);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (! runLoop){
|
||||
LOG_DEBUG(GwLog::LOG,"nothing to do for SPI task, finish");
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
while(true){
|
||||
delay(100);
|
||||
timers.loop();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
|
||||
void initSpiTask(GwApi *api){
|
||||
GwLog *logger=api->getLogger();
|
||||
registerDMS22B(api,sensors);
|
||||
bool addTask=false;
|
||||
for (auto && sensor:sensors){
|
||||
if (sensor->preinit(api)) addTask=true;
|
||||
}
|
||||
if (addTask){
|
||||
api->addUserTask(runSpiTask,"spiTask",3000);
|
||||
}
|
||||
else{
|
||||
LOG_DEBUG(GwLog::LOG,"no SPI sensors defined/active");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
(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 _GWSPITASK_H
|
||||
#define _GWSPITASK_H
|
||||
#include "GwApi.h"
|
||||
void initSpiTask(GwApi *api);
|
||||
DECLARE_INITFUNCTION(initSpiTask);
|
||||
#endif
|
|
@ -0,0 +1,91 @@
|
|||
[
|
||||
{
|
||||
"type": "array",
|
||||
"name": "DMS22B",
|
||||
"replace": [
|
||||
{
|
||||
"b": "1",
|
||||
"i": "11",
|
||||
"n": "0"
|
||||
},
|
||||
{
|
||||
"b": "1",
|
||||
"i": "12",
|
||||
"n": "1"
|
||||
},
|
||||
{
|
||||
"b": "2",
|
||||
"i": "21",
|
||||
"n": "1"
|
||||
},
|
||||
{
|
||||
"b": "2",
|
||||
"i": "22",
|
||||
"n": "22"
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"name": "DMS22B$iact",
|
||||
"label": "DMS22BX$i enable",
|
||||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "Enable the $i. SSI DMS22B rotary encoder (bus $b)",
|
||||
"category": "spisensors$b",
|
||||
"capabilities": {
|
||||
"DMS22B$i": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "DMS22B$iiid",
|
||||
"label": "DMS22B$i N2K iid",
|
||||
"type": "number",
|
||||
"default": "$n",
|
||||
"description": "the N2K instance id for the $i. DMS22B Rotary Encoder ",
|
||||
"category": "spisensors$b",
|
||||
"min": 0,
|
||||
"max": 253,
|
||||
"check": "checkMinMax",
|
||||
"capabilities": {
|
||||
"DMS22B$i": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "DMS22B$ifintv",
|
||||
"label": "DMS22B$i Interval",
|
||||
"type": "number",
|
||||
"default": 2,
|
||||
"description": "Interval(s) to query DMS22B rotation (0.5...10)",
|
||||
"category": "spisensors$b",
|
||||
"min": 0.5,
|
||||
"max": 10,
|
||||
"check": "checkMinMax",
|
||||
"capabilities": {
|
||||
"DMS22B$i": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "DMS22B$izero",
|
||||
"label": "DMS22B$i Zero",
|
||||
"type": "calset",
|
||||
"default": 2048,
|
||||
"description": "Zero position (0...2^bits-1)\nuse the \"C\" button to open a calibrate dialog",
|
||||
"category": "spisensors$b",
|
||||
"capabilities": {
|
||||
"DMS22B$i": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "DMS22B$iinvt",
|
||||
"label": "DMS22BX$i invert",
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "Invert the direction of the $i. SSI DMS22B rotary encoder (bus $b)",
|
||||
"category": "spisensors$b",
|
||||
"capabilities": {
|
||||
"DMS22B$i": "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,14 @@
|
|||
[platformio]
|
||||
#basically for testing purposes
|
||||
[env:m5stack-atom-spidms22b]
|
||||
extends = sensors
|
||||
board = m5stack-atom
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
${sensors.lib_deps}
|
||||
build_flags=
|
||||
-D GWSPI1_CLK=21
|
||||
-D GWSPI1_MISO=25
|
||||
-D GWDMS22B11
|
||||
-D GWDMS22B11_CS=22
|
||||
${env.build_flags}
|
|
@ -332,6 +332,9 @@ public:
|
|||
api->getLogger()->logDebug(GwLog::LOG,"adding user task %s",tname.c_str());
|
||||
return true;
|
||||
}
|
||||
virtual void setCalibrationValue(const String &name, double value){
|
||||
api->setCalibrationValue(name,value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
|
62
src/main.cpp
62
src/main.cpp
|
@ -1,4 +1,5 @@
|
|||
/*
|
||||
(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
|
||||
|
@ -19,6 +20,7 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting
|
|||
#include <Arduino.h>
|
||||
#include "Preferences.h"
|
||||
#include "GwApi.h"
|
||||
#define GW_PINDEFS
|
||||
#include "GwHardware.h"
|
||||
|
||||
#ifndef N2K_LOAD_LEVEL
|
||||
|
@ -69,10 +71,6 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting
|
|||
|
||||
#define MAX_NMEA2000_MESSAGE_SEASMART_SIZE 500
|
||||
#define MAX_NMEA0183_MESSAGE_SIZE MAX_NMEA2000_MESSAGE_SEASMART_SIZE
|
||||
//https://curiouser.cheshireeng.com/2014/08/19/c-compile-time-assert/
|
||||
#define CASSERT(predicate, text) _impl_CASSERT_LINE(predicate,__LINE__)
|
||||
#define _impl_PASTE(a,b) a##b
|
||||
#define _impl_CASSERT_LINE(predicate, line) typedef char _impl_PASTE(assertion_failed_CASSERT_,line)[(predicate)?1:-1];
|
||||
//assert length of firmware name and version
|
||||
CASSERT(strlen(FIRMWARE_TYPE) <= 32, "environment name (FIRMWARE_TYPE) must not exceed 32 chars");
|
||||
CASSERT(strlen(VERSION) <= 32, "VERSION must not exceed 32 chars");
|
||||
|
@ -234,15 +232,38 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg, int sourceId,bool conv
|
|||
});
|
||||
}
|
||||
|
||||
class CalibrationValues {
|
||||
using Map=std::map<String,double>;
|
||||
Map values;
|
||||
SemaphoreHandle_t lock;
|
||||
public:
|
||||
CalibrationValues(){
|
||||
lock=xSemaphoreCreateMutex();
|
||||
}
|
||||
void set(const String &name,double value){
|
||||
GWSYNCHRONIZED(&lock);
|
||||
values[name]=value;
|
||||
}
|
||||
bool get(const String &name, double &value){
|
||||
GWSYNCHRONIZED(&lock);
|
||||
auto it=values.find(name);
|
||||
if (it==values.end()) return false;
|
||||
value=it->second;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ApiImpl : public GwApiInternal
|
||||
{
|
||||
private:
|
||||
int sourceId = -1;
|
||||
std::unique_ptr<CalibrationValues> calibrations;
|
||||
|
||||
public:
|
||||
ApiImpl(int sourceId)
|
||||
{
|
||||
this->sourceId = sourceId;
|
||||
calibrations.reset(new CalibrationValues());
|
||||
}
|
||||
virtual GwRequestQueue *getQueue()
|
||||
{
|
||||
|
@ -331,6 +352,13 @@ public:
|
|||
virtual bool addUserTask(GwUserTaskFunction task,const String Name, int stackSize=2000){
|
||||
return false;
|
||||
}
|
||||
virtual void setCalibrationValue(const String &name, double value){
|
||||
calibrations->set(name,value);
|
||||
}
|
||||
|
||||
bool getCalibrationValue(const String &name,double &value){
|
||||
return calibrations->get(name,value);
|
||||
}
|
||||
};
|
||||
|
||||
bool delayedRestart(){
|
||||
|
@ -382,7 +410,7 @@ public:
|
|||
protected:
|
||||
virtual void processRequest()
|
||||
{
|
||||
GwJsonDocument status(300 +
|
||||
GwJsonDocument status(305 +
|
||||
countNMEA2KIn.getJsonSize()+
|
||||
countNMEA2KOut.getJsonSize() +
|
||||
channels.getJsonSize()+
|
||||
|
@ -390,6 +418,7 @@ protected:
|
|||
);
|
||||
status["version"] = VERSION;
|
||||
status["wifiConnected"] = gwWifi.clientConnected();
|
||||
status["wifiSSID"] = config.getString(GwConfigDefinitions::wifiSSID);
|
||||
status["clientIP"] = WiFi.localIP().toString();
|
||||
status["apIp"] = gwWifi.apIP();
|
||||
size_t bsize=2*sizeof(unsigned long)+1;
|
||||
|
@ -756,6 +785,7 @@ void setup() {
|
|||
logger.setWriter(new DefaultLogWriter());
|
||||
#endif
|
||||
userCodeHandler.startInitTasks(MIN_USER_TASK);
|
||||
channels.preinit();
|
||||
config.stopChanges();
|
||||
//maybe the user code changed the level
|
||||
level=config.getInt(config.logLevel,LOGLEVEL);
|
||||
|
@ -803,11 +833,24 @@ void setup() {
|
|||
|
||||
},
|
||||
handleConfigRequestData);
|
||||
webserver.registerHandler("/api/calibrate",[](AsyncWebServerRequest *request){
|
||||
const String name=request->arg("name");
|
||||
double value;
|
||||
if (! apiImpl->getCalibrationValue(name,value)){
|
||||
request->send(400, "text/plain", "name not found");
|
||||
return;
|
||||
}
|
||||
char buffer[30];
|
||||
snprintf(buffer,29,"%g",value);
|
||||
buffer[29]=0;
|
||||
request->send(200,"text/plain",buffer);
|
||||
});
|
||||
|
||||
webserver.begin();
|
||||
xdrMappings.begin();
|
||||
logger.flush();
|
||||
|
||||
GwConverterConfig converterConfig;
|
||||
converterConfig.init(&config);
|
||||
nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData,
|
||||
[](const tNMEA0183Msg &msg, int sourceId){
|
||||
SendNMEA0183Message(msg,sourceId,false);
|
||||
|
@ -815,7 +858,7 @@ void setup() {
|
|||
,
|
||||
config.getString(config.talkerId,String("GP")),
|
||||
&xdrMappings,
|
||||
config.getInt(config.minXdrInterval,100)
|
||||
converterConfig
|
||||
);
|
||||
|
||||
toN2KConverter= NMEA0183DataToN2K::create(&logger,&boatData,[](const tN2kMsg &msg, int sourceId)->bool{
|
||||
|
@ -824,7 +867,7 @@ void setup() {
|
|||
return true;
|
||||
},
|
||||
&xdrMappings,
|
||||
config.getInt(config.min2KInterval,50)
|
||||
converterConfig
|
||||
);
|
||||
|
||||
NMEA2000.SetN2kCANMsgBufSize(8);
|
||||
|
@ -946,7 +989,8 @@ void loopRun() {
|
|||
preferences.end();
|
||||
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);
|
||||
|
||||
//read channels
|
||||
|
|
|
@ -51,7 +51,8 @@ EXCEPTIONS = [
|
|||
|
||||
PLATFORMS = {
|
||||
"ESP8266": "lx106",
|
||||
"ESP32": "esp32"
|
||||
"ESP32": "esp32",
|
||||
"ESP32S3": "esp32s3"
|
||||
}
|
||||
|
||||
BACKTRACE_REGEX = re.compile(r"(?:\s+(0x40[0-2](?:\d|[a-f]|[A-F]){5}):0x(?:\d|[a-f]|[A-F]){8})\b")
|
||||
|
@ -142,7 +143,7 @@ class ExceptionDataParser(object):
|
|||
return None
|
||||
|
||||
def parse_file(self, file, platform, stack_only=False):
|
||||
if platform == 'ESP32':
|
||||
if platform == 'ESP32' or platform == 'ESP32S3':
|
||||
func = self._parse_backtrace
|
||||
else:
|
||||
func = self._parse_exception
|
||||
|
|
202
web/config.json
202
web/config.json
|
@ -8,6 +8,86 @@
|
|||
"description": "system name, used for the access point and for services",
|
||||
"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",
|
||||
"label": "NMEA0183 ID",
|
||||
|
@ -100,87 +180,7 @@
|
|||
"ZV"
|
||||
],
|
||||
"description": "the talkerId used in generated NMEA0183 records",
|
||||
"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"
|
||||
"category": "converter"
|
||||
},
|
||||
{
|
||||
"name": "minXdrInterval",
|
||||
|
@ -210,6 +210,46 @@
|
|||
"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"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"label":"stb rudder instance",
|
||||
"type": "number",
|
||||
"default": "0",
|
||||
"check": "checkMinMax",
|
||||
"min": 0,
|
||||
"max": 253,
|
||||
"description": "the n2k instance to be used as starboard(main) rudder 0...253",
|
||||
"category": "converter"
|
||||
},
|
||||
{
|
||||
"name": "portRudderI",
|
||||
"label":"port rudder instance",
|
||||
"type": "number",
|
||||
"default": "-1",
|
||||
"check": "checkMinMax",
|
||||
"min": -1,
|
||||
"max": 253,
|
||||
"description": "the n2k instance to be used as port rudder 0...253, -1 to disable",
|
||||
"category": "converter"
|
||||
},
|
||||
{
|
||||
"name": "usbActisense",
|
||||
"label": "USB mode",
|
||||
|
|
|
@ -114,6 +114,9 @@ body {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.value button {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
.hidden{
|
||||
display: none !important;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
</div>
|
||||
<div class="row ">
|
||||
<span class="label">wifi client connected</span>
|
||||
<span class="value" id="wifiConnected">---</span>
|
||||
<span class="value" id="wifiConnected">---</span> [<span class="value" id="wifiSSID">---</span>]
|
||||
</div>
|
||||
<div class="row even">
|
||||
<span class="label">wifi client IP</span>
|
||||
|
@ -102,7 +102,7 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<span class="label">chip type</span>
|
||||
<span class="value status-chiptype">---</span>
|
||||
<span class="value status-chipid">---</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">currentVersion</span>
|
||||
|
@ -131,7 +131,6 @@
|
|||
AHA
|
||||
</div>
|
||||
<div class="overlayButtons">
|
||||
<button id="hideOverlay">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -190,6 +189,17 @@
|
|||
<div class="hidden">
|
||||
<a id="downloadXdr"></a>
|
||||
</div>
|
||||
<div class="hidden" id="calset">
|
||||
<h2 class="heading"></h2>
|
||||
<p class="val"></p>
|
||||
</div>
|
||||
<div class="hidden" id="calval">
|
||||
<h2 class="heading"></h2>
|
||||
<p class="val"></p>
|
||||
<label>config
|
||||
<input type="number"/>
|
||||
</label>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
119
web/index.js
119
web/index.js
|
@ -346,11 +346,15 @@ function createCounterDisplay(parent,label,key,isEven){
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
function validKey(key){
|
||||
if (! key) return;
|
||||
return key.replace(/[^a-z_:A-Z0-9-]/g,'');
|
||||
}
|
||||
function updateMsgDetails(key, details) {
|
||||
forEl('.msgDetails', function (frame) {
|
||||
if (frame.getAttribute('id') !== key) return;
|
||||
for (let k in details) {
|
||||
k=validKey(k);
|
||||
let el = frame.querySelector("[data-id=\"" + k + "\"] ");
|
||||
if (!el) {
|
||||
el = addEl('div', 'row', frame);
|
||||
|
@ -371,8 +375,13 @@ function updateMsgDetails(key, details) {
|
|||
});
|
||||
}
|
||||
|
||||
function showOverlay(text, isHtml) {
|
||||
function showOverlay(text, isHtml,buttons) {
|
||||
let el = document.getElementById('overlayContent');
|
||||
if (text instanceof Object){
|
||||
el.textContent='';
|
||||
el.appendChild(text);
|
||||
}
|
||||
else {
|
||||
if (isHtml) {
|
||||
el.innerHTML = text;
|
||||
el.classList.remove("text");
|
||||
|
@ -381,12 +390,22 @@ function showOverlay(text, isHtml) {
|
|||
el.textContent = text;
|
||||
el.classList.add("text");
|
||||
}
|
||||
}
|
||||
buttons=(buttons?buttons:[]).concat([{label:"Close",click:hideOverlay}]);
|
||||
let container = document.getElementById('overlayContainer');
|
||||
let btframe=container.querySelector('.overlayButtons');
|
||||
btframe.textContent='';
|
||||
buttons.forEach((btconfig)=>{
|
||||
let bt=addEl('button','',btframe,btconfig.label);
|
||||
bt.addEventListener("click",btconfig.click);
|
||||
});
|
||||
container.classList.remove('hidden');
|
||||
}
|
||||
function hideOverlay() {
|
||||
let container = document.getElementById('overlayContainer');
|
||||
container.classList.add('hidden');
|
||||
let el = document.getElementById('overlayContent');
|
||||
el.textContent='';
|
||||
}
|
||||
function checkChange(el, row,name) {
|
||||
let loaded = el.getAttribute('data-loaded');
|
||||
|
@ -456,6 +475,94 @@ function checkCondition(element){
|
|||
if (visible) row.classList.remove('hidden');
|
||||
else row.classList.add('hidden');
|
||||
}
|
||||
let caliv=0;
|
||||
function createCalSetInput(configItem,frame,clazz){
|
||||
let el = addEl('input',clazz,frame);
|
||||
let cb = addEl('button','',frame,'C');
|
||||
//el.disabled=true;
|
||||
cb.addEventListener('click',(ev)=>{
|
||||
let cs=document.getElementById("calset").cloneNode(true);
|
||||
cs.classList.remove("hidden");
|
||||
cs.querySelector(".heading").textContent=configItem.label||configItem.name;
|
||||
let vel=cs.querySelector(".val");
|
||||
if (caliv != 0) window.clearInterval(caliv);
|
||||
caliv=window.setInterval(()=>{
|
||||
if (document.body.contains(cs)){
|
||||
fetch("/api/calibrate?name="+encodeURIComponent(configItem.name))
|
||||
.then((r)=>r.text())
|
||||
.then((txt)=>{
|
||||
if (txt != vel.textContent){
|
||||
vel.textContent=txt;
|
||||
}
|
||||
})
|
||||
.catch((e)=>{
|
||||
alert(e);
|
||||
hideOverlay();
|
||||
window.clearInterval(caliv);
|
||||
})
|
||||
}
|
||||
else{
|
||||
window.clearInterval(caliv);
|
||||
}
|
||||
},200);
|
||||
showOverlay(cs,false,[{label:'Set',click:()=>{
|
||||
el.value=vel.textContent;
|
||||
let cev=new Event('change');
|
||||
el.dispatchEvent(cev);
|
||||
}}]);
|
||||
})
|
||||
el.setAttribute('name', configItem.name)
|
||||
return el;
|
||||
}
|
||||
function createCalValInput(configItem,frame,clazz){
|
||||
let el = addEl('input',clazz,frame);
|
||||
let cb = addEl('button','',frame,'C');
|
||||
//el.disabled=true;
|
||||
cb.addEventListener('click',(ev)=>{
|
||||
const sv=function(val,cfg){
|
||||
if (configItem.eval){
|
||||
let v=parseFloat(val);
|
||||
let c=parseFloat(cfg);
|
||||
return(eval(configItem.eval));
|
||||
}
|
||||
return v;
|
||||
};
|
||||
let cs=document.getElementById("calval").cloneNode(true);
|
||||
cs.classList.remove("hidden");
|
||||
cs.querySelector(".heading").textContent=configItem.label||configItem.name;
|
||||
let vel=cs.querySelector(".val");
|
||||
let vinp=cs.querySelector("input");
|
||||
vinp.value=el.value;
|
||||
if (caliv != 0) window.clearInterval(caliv);
|
||||
caliv=window.setInterval(()=>{
|
||||
if (document.body.contains(cs)){
|
||||
fetch("/api/calibrate?name="+encodeURIComponent(configItem.name))
|
||||
.then((r)=>r.text())
|
||||
.then((txt)=>{
|
||||
txt=sv(txt,vinp.value);
|
||||
if (txt != vel.textContent){
|
||||
vel.textContent=txt;
|
||||
}
|
||||
})
|
||||
.catch((e)=>{
|
||||
alert(e);
|
||||
hideOverlay();
|
||||
window.clearInterval(caliv);
|
||||
})
|
||||
}
|
||||
else{
|
||||
window.clearInterval(caliv);
|
||||
}
|
||||
},200);
|
||||
showOverlay(cs,false,[{label:'Set',click:()=>{
|
||||
el.value=vinp.value;
|
||||
let cev=new Event('change');
|
||||
el.dispatchEvent(cev);
|
||||
}}]);
|
||||
})
|
||||
el.setAttribute('name', configItem.name)
|
||||
return el;
|
||||
}
|
||||
function createInput(configItem, frame,clazz) {
|
||||
let el;
|
||||
if (configItem.type === 'boolean' || configItem.type === 'list' || configItem.type == 'boatData') {
|
||||
|
@ -492,6 +599,12 @@ function createInput(configItem, frame,clazz) {
|
|||
if (configItem.type === 'xdr'){
|
||||
return createXdrInput(configItem,frame,clazz);
|
||||
}
|
||||
if (configItem.type === "calset"){
|
||||
return createCalSetInput(configItem,frame,clazz);
|
||||
}
|
||||
if (configItem.type === "calval"){
|
||||
return createCalValInput(configItem,frame,clazz);
|
||||
}
|
||||
el = addEl('input',clazz,frame);
|
||||
if (configItem.readOnly) el.setAttribute('disabled',true);
|
||||
el.setAttribute('name', configItem.name)
|
||||
|
@ -1506,7 +1619,9 @@ function createDashboard() {
|
|||
frame.innerHTML = '';
|
||||
}
|
||||
function sourceName(v){
|
||||
if (v == 0) return "N2K";
|
||||
for (let n in channelList){
|
||||
if (v == channelList[n].id) return n;
|
||||
if (v >= channelList[n].id && v <= channelList[n].max){
|
||||
return n;
|
||||
}
|
||||
|
|
|
@ -36,86 +36,122 @@
|
|||
#
|
||||
#
|
||||
types:
|
||||
- &m5base
|
||||
type: select
|
||||
target: define
|
||||
label: 'M5 Atom light Base'
|
||||
key: m5lightbase
|
||||
values:
|
||||
- label: "CAN KIT"
|
||||
value: M5_CAN_KIT
|
||||
description: "M5 Stack CAN Kit"
|
||||
url: "https://docs.m5stack.com/en/atom/atom_can"
|
||||
resource: can
|
||||
- value: M5_SERIAL_KIT_232
|
||||
description: "M5 Stack RS232 Base"
|
||||
label: "Atomic RS232 Base"
|
||||
url: "https://docs.m5stack.com/en/atom/Atomic%20RS232%20Base"
|
||||
resource: serial
|
||||
- value: M5_SERIAL_KIT_485
|
||||
description: "M5 Stack RS485 Base"
|
||||
label: "Atomic RS485 Base"
|
||||
url: "https://docs.m5stack.com/en/atom/Atomic%20RS485%20Base"
|
||||
resource: serial
|
||||
- value: M5_GPS_KIT
|
||||
description: "M5 Stack Gps Kit"
|
||||
label: "Gps Base"
|
||||
url: "https://docs.m5stack.com/en/atom/atomicgps"
|
||||
resource: serial
|
||||
|
||||
- &m5groovei2c
|
||||
type: frame
|
||||
key: m5groovei2c
|
||||
key: m5groovei2c#grv#
|
||||
label: "M5 I2C Groove Units"
|
||||
children:
|
||||
- label: "M5 ENV3"
|
||||
type: checkbox
|
||||
key: m5env3
|
||||
key: m5env3#grv#
|
||||
target: define
|
||||
url: "https://docs.m5stack.com/en/unit/envIII"
|
||||
description: "M5 sensor module temperature, humidity, pressure"
|
||||
values:
|
||||
- value: M5_ENV3
|
||||
- value: M5_ENV3#grv#
|
||||
key: true
|
||||
resource: qmp69881#grv#1,sht3x#grv#1
|
||||
- type: checkbox
|
||||
label: SHT3X-1
|
||||
description: "SHT30 temperature and humidity sensor 0x44"
|
||||
key: sht3xg1
|
||||
target: define
|
||||
url: "https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/SHT3x_Datasheet_digital.pdf"
|
||||
values:
|
||||
- key: true
|
||||
value: GWSHT3XG1#grv#
|
||||
resource: sht3x#grv#1
|
||||
- type: checkbox
|
||||
label: SHT3X-2
|
||||
description: "SHT30 temperature and humidity sensor 0x45"
|
||||
key: sht3xg2
|
||||
target: define
|
||||
url: "https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/SHT3x_Datasheet_digital.pdf"
|
||||
values:
|
||||
- key: true
|
||||
value: GWSHT3XG2#grv#
|
||||
resource: sht3x#grv#2
|
||||
- type: checkbox
|
||||
label: QMP6988-1
|
||||
description: "QMP6988 pressure sensor addr 86"
|
||||
key: qmp69881g1
|
||||
target: define
|
||||
url: "https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/enviii/QMP6988%20Datasheet.pdf"
|
||||
values:
|
||||
- key: true
|
||||
value: GWQMP6988G1#grv#
|
||||
resource: qmp69881#grv#1
|
||||
- type: checkbox
|
||||
label: QMP6988-2
|
||||
description: "QMP6988 pressure sensor addr 112"
|
||||
key: qmp69882g2
|
||||
target: define
|
||||
url: "https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/unit/enviii/QMP6988%20Datasheet.pdf"
|
||||
values:
|
||||
- key: true
|
||||
value: GWQMP6988G2#grv#
|
||||
resource: qmp69881#grv#2
|
||||
- type: checkbox
|
||||
label: BME280-1
|
||||
description: "BME280 temperature/humidity/pressure sensor 0x76"
|
||||
key: bme2801g1
|
||||
target: define
|
||||
url: "https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf"
|
||||
values:
|
||||
- key: true
|
||||
value: GWBME280G1#grv#
|
||||
resource: bme280#grv#1
|
||||
- type: checkbox
|
||||
label: BME280-2
|
||||
description: "BME280 temperature/humidity/pressure sensor 0x77"
|
||||
key: bme2802
|
||||
target: define
|
||||
url: "https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf"
|
||||
values:
|
||||
- key: true
|
||||
value: GWBME280G2#grv#
|
||||
resource: bme280#grv#2
|
||||
|
||||
|
||||
- &m5groovecan
|
||||
type: select
|
||||
key: m5groovecan
|
||||
key: m5groovecan#grv#
|
||||
target: define
|
||||
label: "M5 Groove CAN Units"
|
||||
values:
|
||||
- label: "CAN Unit"
|
||||
url: "https://docs.m5stack.com/en/unit/can"
|
||||
description: "M5 Can unit"
|
||||
value: M5_CANUNIT
|
||||
value: M5_CANUNIT#grv#
|
||||
resource: can
|
||||
- &m5grooveserial
|
||||
type: select
|
||||
label: "M5 Groove Serial Unit"
|
||||
target: define
|
||||
key: m5grooveserial
|
||||
key: m5grooveserial#grv#
|
||||
values:
|
||||
- label: "RS485"
|
||||
key: unit485
|
||||
value: SERIAL_GROOVE_485
|
||||
value: SERIAL_GROOVE_485#grv#
|
||||
description: "M5 RS485 unit"
|
||||
url: "https://docs.m5stack.com/en/unit/rs485"
|
||||
resource: serial
|
||||
- label: "Tail485"
|
||||
value: SERIAL_GROOVE_485
|
||||
value: SERIAL_GROOVE_485#grv#
|
||||
key: tail485
|
||||
description: "M5 Tail 485"
|
||||
url: "https://docs.m5stack.com/en/atom/tail485"
|
||||
resource: serial
|
||||
- label: "Gps Unit"
|
||||
value: M5_GPS_UNIT
|
||||
value: M5_GPS_UNIT#grv#
|
||||
description: "M5 Gps Unit"
|
||||
url: "https://docs.m5stack.com/en/unit/gps"
|
||||
resource: serial
|
||||
|
||||
- &m5groove
|
||||
type: select
|
||||
key: m5groove
|
||||
label: 'M5 groove type'
|
||||
key: m5groove#grv#
|
||||
label: 'M5 groove#grv# type '
|
||||
help: 'Select the functionality that should be available at the M5 groove pins'
|
||||
values:
|
||||
- key: 'CAN'
|
||||
|
@ -131,7 +167,9 @@ types:
|
|||
type: dropdown
|
||||
resource: "gpio:"
|
||||
help: 'Select the number of the GPIO pin for this function'
|
||||
values:
|
||||
values: "#gpiopinv#"
|
||||
|
||||
- &gpiopinv
|
||||
- {label: unset,value:}
|
||||
- {label: "0: Low at boot!",value: 0}
|
||||
- 1
|
||||
|
@ -164,7 +202,9 @@ types:
|
|||
type: dropdown
|
||||
resource: "gpio:"
|
||||
help: 'Select the number of the GPIO pin for this function'
|
||||
values:
|
||||
values: "#gpiopinv#"
|
||||
|
||||
- &gpioinputv
|
||||
- {label: unset,value:}
|
||||
- {label: "0: Low at boot!",value: 0}
|
||||
- 1
|
||||
|
@ -197,6 +237,15 @@ types:
|
|||
- 38
|
||||
- 39
|
||||
|
||||
- &protogpio
|
||||
- {label: unset,value:}
|
||||
- PPIN19
|
||||
- PPIN21
|
||||
- PPIN22
|
||||
- PPIN23
|
||||
- PPIN25
|
||||
- PPIN33
|
||||
|
||||
- &serialRX
|
||||
<<: *gpioinput
|
||||
key: RX
|
||||
|
@ -249,6 +298,7 @@ types:
|
|||
type: checkbox
|
||||
label: 'Serial 1'
|
||||
key: serial1
|
||||
resource: serial1
|
||||
base:
|
||||
serial: GWSERIAL_
|
||||
values: *serialValues
|
||||
|
@ -257,6 +307,7 @@ types:
|
|||
type: checkbox
|
||||
label: 'Serial 2'
|
||||
key: serial2
|
||||
resource: serial2
|
||||
base:
|
||||
serial: GWSERIAL2_
|
||||
values: *serialValues
|
||||
|
@ -374,6 +425,7 @@ types:
|
|||
type: checkbox
|
||||
label: "I2C #busname#"
|
||||
key: "i2c#busname#"
|
||||
resource: "i2c#busname#"
|
||||
description: "I2C Bus #busname#"
|
||||
values:
|
||||
- key: true
|
||||
|
@ -443,6 +495,136 @@ types:
|
|||
- key: true
|
||||
value: GWBME280#busname#2
|
||||
|
||||
- &spisensors
|
||||
type: checkbox
|
||||
label: "SPI/SSI #busname#"
|
||||
key: "spi#busname#"
|
||||
resource: "spi#busname#"
|
||||
description: "SPI(SSI) Bus #busname#"
|
||||
values:
|
||||
- key: true
|
||||
children:
|
||||
- <<: *gpiopin
|
||||
label: CLK
|
||||
key: clk
|
||||
mandatory: true
|
||||
target: "define:GWSPI#bus#_CLK"
|
||||
- <<: *gpiopin
|
||||
label: MISO
|
||||
key: miso
|
||||
mandatory: false
|
||||
target: "define:GWSPI#bus#_MISO"
|
||||
- <<: *gpiopin
|
||||
label: MOSI
|
||||
key: mosi
|
||||
mandatory: false
|
||||
target: "define:GWSPI#bus#_MOSI"
|
||||
description: "GPIO pin for MOSI, not necessary for SSI"
|
||||
- type: checkbox
|
||||
label: GWDMS22B-#busname#-1
|
||||
description: "DMS22B rotatory encoder (SSI)"
|
||||
key: dms22b#busname#1
|
||||
target: define
|
||||
url: "https://www.mouser.de/datasheet/2/54/bour_s_a0011704065_1-2262614.pdf"
|
||||
values:
|
||||
- key: true
|
||||
value: GWDMS22B#busname#1
|
||||
children:
|
||||
- <<: *gpiopin
|
||||
label: CS
|
||||
key: dms22b#busname#1cs
|
||||
description: "chip select pin, only necessary for multiple devices on this bus"
|
||||
target: "define:GWDMS22B#busname#1_CS"
|
||||
- type: checkbox
|
||||
label: GWDMS22B-#busname#-2
|
||||
description: "DMS22B rotatory encoder (SSI)"
|
||||
key: dms22b#busname#2
|
||||
target: define
|
||||
url: "https://www.mouser.de/datasheet/2/54/bour_s_a0011704065_1-2262614.pdf"
|
||||
values:
|
||||
- key: true
|
||||
value: GWDMS22B#busname#2
|
||||
children:
|
||||
- <<: *gpiopin
|
||||
label: CS
|
||||
key: dms22b#busname#2cs
|
||||
description: "chip select pin, only necessary for multiple devices on this bus"
|
||||
target: "define:GWDMS22B#busname#2_CS"
|
||||
|
||||
- &m5protochildren
|
||||
- *serial1
|
||||
- *serial2
|
||||
- *can
|
||||
- <<: *iicsensors
|
||||
base:
|
||||
busname: "1"
|
||||
bus: ""
|
||||
- <<: *iicsensors
|
||||
base:
|
||||
busname: "2"
|
||||
bus: "2"
|
||||
- <<: *spisensors
|
||||
base:
|
||||
busname: "1"
|
||||
bus: "1"
|
||||
- <<: *spisensors
|
||||
base:
|
||||
busname: "2"
|
||||
bus: "2"
|
||||
- &m5pabcchildren
|
||||
- <<: *m5groove
|
||||
base:
|
||||
grv: _A
|
||||
- <<: *m5groove
|
||||
base:
|
||||
grv: _B
|
||||
- <<: *m5groove
|
||||
base:
|
||||
grv: _C
|
||||
|
||||
|
||||
|
||||
- &m5base
|
||||
type: select
|
||||
target: define
|
||||
label: 'M5 Atom light Base'
|
||||
key: m5lightbase
|
||||
values:
|
||||
- label: "CAN KIT"
|
||||
value: M5_CAN_KIT
|
||||
description: "M5 Stack CAN Kit"
|
||||
url: "https://docs.m5stack.com/en/atom/atom_can"
|
||||
resource: can
|
||||
- value: M5_SERIAL_KIT_232
|
||||
description: "M5 Stack RS232 Base"
|
||||
label: "Atomic RS232 Base"
|
||||
url: "https://docs.m5stack.com/en/atom/Atomic%20RS232%20Base"
|
||||
resource: serial
|
||||
- value: M5_SERIAL_KIT_485
|
||||
description: "M5 Stack RS485 Base"
|
||||
label: "Atomic RS485 Base"
|
||||
url: "https://docs.m5stack.com/en/atom/Atomic%20RS485%20Base"
|
||||
resource: serial
|
||||
- value: M5_GPS_KIT
|
||||
description: "M5 Stack Gps Kit"
|
||||
label: "Gps Base"
|
||||
url: "https://docs.m5stack.com/en/atom/atomicgps"
|
||||
resource: serial
|
||||
- value: M5_PROTO_HUB
|
||||
description: "M5 Stack HUB PROTO"
|
||||
url: "https://docs.m5stack.com/en/atom/atomhub"
|
||||
label: "Hub Proto"
|
||||
base:
|
||||
gpioinputv: *protogpio
|
||||
gpiopinv: *protogpio
|
||||
children:
|
||||
*m5protochildren
|
||||
- value: M5_PORTABC
|
||||
description: "M5 Stack Port ABC extension base"
|
||||
url: "https://docs.m5stack.com/en/unit/AtomPortABC"
|
||||
label: "ABC Ext"
|
||||
children:
|
||||
*m5pabcchildren
|
||||
|
||||
resources:
|
||||
default: &esp32default
|
||||
|
@ -457,6 +639,10 @@ config:
|
|||
target: environment
|
||||
label: 'Board'
|
||||
key: board
|
||||
base:
|
||||
gpiopinv: *gpiopinv
|
||||
gpioinputv: *gpioinputv
|
||||
grv: ""
|
||||
values:
|
||||
- value: m5stack-atom-generic
|
||||
label: m5stack-atom
|
||||
|
@ -502,3 +688,11 @@ config:
|
|||
base:
|
||||
busname: "2"
|
||||
bus: "2"
|
||||
- <<: *spisensors
|
||||
base:
|
||||
busname: "1"
|
||||
bus: "1"
|
||||
- <<: *spisensors
|
||||
base:
|
||||
busname: "2"
|
||||
bus: "2"
|
|
@ -119,7 +119,7 @@ class PipelineInfo{
|
|||
.then((st)=>{
|
||||
if (queryPipeline !== currentPipeline.id) return;
|
||||
let stid=st.pipeline_id||st.id;
|
||||
if (currentPipeline.id !== stid) return;
|
||||
if (stid !== undefined && currentPipeline.id !== stid) return;
|
||||
if (st.status === undefined) st.status=st.state;
|
||||
currentPipeline.update(st);
|
||||
updateStatus();
|
||||
|
@ -516,6 +516,11 @@ class PipelineInfo{
|
|||
});
|
||||
}
|
||||
}
|
||||
if (expandedValues.length > 0 && config.type === 'display'){
|
||||
let cb=addEl('div','t'+config.type,inputFrame);
|
||||
addDescription(config,inputFrame);
|
||||
initialConfig=expandedValues[0];
|
||||
}
|
||||
let childFrame=addEl('div','childFrame',frame);
|
||||
if (initialConfig !== undefined){
|
||||
callback(initialConfig,true,childFrame);
|
||||
|
@ -573,7 +578,7 @@ class PipelineInfo{
|
|||
(child,initial,opt_frame)=>{
|
||||
if(cfg.key !== undefined) removeSelectors(name,!initial);
|
||||
if (! initial) isModified=true;
|
||||
buildSelectors(name,child.children,initial,currentBase,opt_frame||childFrame);
|
||||
buildSelectors(name,child.children,initial,Object.assign({},currentBase,child.base),opt_frame||childFrame);
|
||||
if (cfg.key !== undefined) configStruct[name]={cfg:child,base:currentBase};
|
||||
buildValues(initial);
|
||||
})
|
||||
|
@ -583,9 +588,18 @@ class PipelineInfo{
|
|||
if (! base) return str;
|
||||
if (typeof(str) === 'string'){
|
||||
for (let k in base){
|
||||
if (typeof(base[k]) !== 'string'){
|
||||
//special replacement
|
||||
//for complete parts
|
||||
if (str === '#'+k+'#'){
|
||||
return base[k];
|
||||
}
|
||||
}
|
||||
else{
|
||||
let r=new RegExp("#"+k+"#","g");
|
||||
str=str.replace(r,base[k]);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
if (str instanceof Array){
|
||||
|
@ -598,7 +612,8 @@ class PipelineInfo{
|
|||
if (str instanceof Object){
|
||||
let rt={};
|
||||
for (let k in str){
|
||||
rt[k]=replaceValues(str[k],base);
|
||||
if (k == 'children') rt[k]=str[k];
|
||||
else rt[k]=replaceValues(str[k],base);
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
|
@ -637,12 +652,15 @@ class PipelineInfo{
|
|||
}
|
||||
if (round < 1) continue;
|
||||
if (struct.resource){
|
||||
let resList=currentResources[struct.resource];
|
||||
let splitted=struct.resource.split(",");
|
||||
splitted.forEach((resource) => {
|
||||
let resList = currentResources[resource];
|
||||
if (!resList) {
|
||||
resList = [];
|
||||
currentResources[struct.resource]=resList;
|
||||
currentResources[resource] = resList;
|
||||
}
|
||||
resList.push(struct);
|
||||
});
|
||||
}
|
||||
if (target === 'define') {
|
||||
flags += " -D" + struct.value;
|
||||
|
@ -665,11 +683,12 @@ class PipelineInfo{
|
|||
for (let k in currentResources){
|
||||
let ak=k.replace(/:.*/,'');
|
||||
let resList=currentResources[k];
|
||||
if (allowedResources[ak] !== undefined){
|
||||
if (resList.length > allowedResources[ak]){
|
||||
errors+=" more than "+allowedResources[ak]+" "+k+" device(s) used";
|
||||
}
|
||||
let allowed=allowedResources[ak];
|
||||
if (allowed === undefined) allowed=1;
|
||||
if (resList.length > allowed){
|
||||
errors+=" more than "+allowed+" "+k+" device(s) used";
|
||||
}
|
||||
|
||||
}
|
||||
if (errors){
|
||||
setValue('configError',errors);
|
||||
|
|
Loading…
Reference in New Issue