diff --git a/Readme.md b/Readme.md
index 6d42c98..e86a938 100644
--- a/Readme.md
+++ b/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
+ 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
diff --git a/doc/Hardware.md b/doc/Hardware.md
index 07d7694..af4c855 100644
--- a/doc/Hardware.md
+++ b/doc/Hardware.md
@@ -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
diff --git a/doc/Sensors.md b/doc/Sensors.md
new file mode 100644
index 0000000..faf0ec6
--- /dev/null
+++ b/doc/Sensors.md
@@ -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]
diff --git a/extra_script.py b/extra_script.py
index ea5a8f7..7713451 100644
--- a/extra_script.py
+++ b/extra_script.py
@@ -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)])
diff --git a/lib/api/GwApi.h b/lib/api/GwApi.h
index 95892cf..d4d0716 100644
--- a/lib/api/GwApi.h
+++ b/lib/api/GwApi.h
@@ -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
diff --git a/lib/boatData/GwBoatData.h b/lib/boatData/GwBoatData.h
index 67052dc..6d3ab19 100644
--- a/lib/boatData/GwBoatData.h
+++ b/lib/boatData/GwBoatData.h
@@ -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
diff --git a/lib/channel/GwChannel.cpp b/lib/channel/GwChannel.cpp
index ef9d64e..93a2da3 100644
--- a/lib/channel/GwChannel.cpp
+++ b/lib/channel/GwChannel.cpp
@@ -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,":"";
diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp
index c094a0a..a118f13 100644
--- a/lib/channel/GwChannelList.cpp
+++ b/lib/channel/GwChannelList.cpp
@@ -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 serialInits;
+
+#define CFG_SERIAL(ser,...) \
+ __MSG("serial config " #ser); \
+ static GwInitializer __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
+ class SerialWrapper : public GwChannelList::SerialWrapperBase{
+ private:
+ template
+ 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
+ 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(&Serial1,SERIAL1_CHANNEL_ID),type,rx,tx);
+ return;
+ }
+ if (id == 2){
+ addSerial(new SerialWrapper(&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(HardwareSerial *serialStream,int id,const String &mode,int rx,int 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;
+ }
+ }
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
+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(&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(&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(&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(&Serial2,SERIAL2_CHANNEL_ID),GWSERIAL2_MODE,GWSERIAL2_RX,GWSERIAL2_TX);
#endif
#endif
//tcp client
diff --git a/lib/channel/GwChannelList.h b/lib/channel/GwChannelList.h
index 1bddfca..373dc6a 100644
--- a/lib/channel/GwChannelList.h
+++ b/lib/channel/GwChannelList.h
@@ -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 ChannelList;
@@ -30,12 +36,16 @@ class GwChannelList{
std::map 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 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
diff --git a/lib/config/GWConfig.cpp b/lib/config/GWConfig.cpp
index 28528c4..e53870a 100644
--- a/lib/config/GWConfig.cpp
+++ b/lib/config/GWConfig.cpp
@@ -4,6 +4,13 @@
#include
#include
#include
+using CfgInit=std::function;
+static std::vector cfgInits;
+#define CFG_INIT(name,value,mode) \
+ __MSG("config set " #name " " #value " " #mode); \
+ static GwInitializer _ ## 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;
}
diff --git a/lib/config/GWConfig.h b/lib/config/GWConfig.h
index 521d7a2..eb2a095 100644
--- a/lib/config/GWConfig.h
+++ b/lib/config/GWConfig.h
@@ -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){
diff --git a/lib/config/GwConfigItem.h b/lib/config/GwConfigItem.h
index 12f5e97..9370158 100644
--- a/lib/config/GwConfigItem.h
+++ b/lib/config/GwConfigItem.h
@@ -2,7 +2,6 @@
#define _GWCONFIGITEM_H
#include "WString.h"
#include
-
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
+class GwInitializer{
+ public:
+ using List=std::vector;
+ GwInitializer(List &l,F f){
+ l.push_back(f);
+ }
+};
+
#endif
\ No newline at end of file
diff --git a/lib/config/GwConverterConfig.h b/lib/config/GwConverterConfig.h
new file mode 100644
index 0000000..a3e9c81
--- /dev/null
+++ b/lib/config/GwConverterConfig.h
@@ -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
\ No newline at end of file
diff --git a/lib/gwwebserver/GwWebServer.cpp b/lib/gwwebserver/GwWebServer.cpp
index 6261c42..67df54f 100644
--- a/lib/gwwebserver/GwWebServer.cpp
+++ b/lib/gwwebserver/GwWebServer.cpp
@@ -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){
diff --git a/lib/gwwebserver/GwWebServer.h b/lib/gwwebserver/GwWebServer.h
index 795988e..84c265a 100644
--- a/lib/gwwebserver/GwWebServer.h
+++ b/lib/gwwebserver/GwWebServer.h
@@ -11,10 +11,12 @@ class GwWebServer{
GwLog *logger;
public:
typedef GwRequestMessage *(RequestCreator)(AsyncWebServerRequest *request);
+ using HandlerFunction=std::function;
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;}
diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h
index ae4e640..8306409 100644
--- a/lib/hardware/GwHardware.h
+++ b/lib/hardware/GwHardware.h
@@ -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
diff --git a/lib/hardware/GwM5Base.h b/lib/hardware/GwM5Base.h
new file mode 100644
index 0000000..b5cc77a
--- /dev/null
+++ b/lib/hardware/GwM5Base.h
@@ -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
\ No newline at end of file
diff --git a/lib/hardware/GwM5Grove.h b/lib/hardware/GwM5Grove.h
new file mode 100644
index 0000000..220ee01
--- /dev/null
+++ b/lib/hardware/GwM5Grove.h
@@ -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
\ No newline at end of file
diff --git a/lib/hardware/GwM5Grove.in b/lib/hardware/GwM5Grove.in
new file mode 100644
index 0000000..6d9ce21
--- /dev/null
+++ b/lib/hardware/GwM5Grove.in
@@ -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
diff --git a/lib/iictask/GwBME280.cpp b/lib/iictask/GwBME280.cpp
index 9804e7e..12dcc7e 100644
--- a/lib/iictask/GwBME280.cpp
+++ b/lib/iictask/GwBME280.cpp
@@ -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
#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
diff --git a/lib/iictask/GwBME280.h b/lib/iictask/GwBME280.h
index 03bfe57..6da8c44 100644
--- a/lib/iictask/GwBME280.h
+++ b/lib/iictask/GwBME280.h
@@ -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
\ No newline at end of file
diff --git a/lib/iictask/GwIicSensors.h b/lib/iictask/GwIicSensors.h
index 313e82b..dcb7f3f 100644
--- a/lib/iictask/GwIicSensors.h
+++ b/lib/iictask/GwIicSensors.h
@@ -4,14 +4,17 @@
#include "N2kMessages.h"
#include "GwXdrTypeMappings.h"
#include "GwHardware.h"
+#include "GwSensor.h"
#ifdef _GWIIC
#include
#else
class TwoWire;
#endif
-#define CFG_GET(name,prefix) \
- cfg->getValue(name, GwConfigDefinitions::prefix ## name)
+using BusType=TwoWire;
+using IICSensorList=SensorList;
+using IICSensorBase=SensorBase;
+
template
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{
- 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::vector;
-};
-
-#define CHECK_IIC1() checkDef(GWIIC_SCL,GWIIC_SDA)
-#define CHECK_IIC2() checkDef(GWIIC_SCL2,GWIIC_SDA2)
+#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
\ No newline at end of file
diff --git a/lib/iictask/GwIicTask.cpp b/lib/iictask/GwIicTask.cpp
index 51f3633..fee671f 100644
--- a/lib/iictask/GwIicTask.cpp
+++ b/lib/iictask/GwIicTask.cpp
@@ -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 iicGroveList;
+#define GROOVE_IIC(base, grove, suffix) \
+ static GwInitializer 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