From 8a5bdd710e3d3336d929ea8367f606ce83f5adee Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 1 Mar 2024 14:49:36 +0100 Subject: [PATCH 01/35] update readme --- Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.md b/Readme.md index 6d42c98..da727ef 100644 --- a/Readme.md +++ b/Readme.md @@ -169,6 +169,7 @@ Changelog ********** * 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 From b3d065abd1cba41c44eb069d7498017fa5fca3f6 Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 1 Mar 2024 15:13:11 +0100 Subject: [PATCH 02/35] make SensorBase more generic --- lib/iictask/GwBME280.cpp | 6 ++--- lib/iictask/GwBME280.h | 2 +- lib/iictask/GwIicSensors.h | 31 ++++-------------------- lib/iictask/GwIicTask.cpp | 4 ++-- lib/iictask/GwQMP6988.cpp | 6 ++--- lib/iictask/GwQMP6988.h | 2 +- lib/iictask/GwSHT3X.cpp | 6 ++--- lib/iictask/GwSHT3X.h | 2 +- lib/sensors/GwSensor.h | 49 ++++++++++++++++++++++++++++++++++++++ src/main.cpp | 1 + 10 files changed, 68 insertions(+), 41 deletions(-) create mode 100644 lib/sensors/GwSensor.h diff --git a/lib/iictask/GwBME280.cpp b/lib/iictask/GwBME280.cpp index 9804e7e..498bf1e 100644 --- a/lib/iictask/GwBME280.cpp +++ b/lib/iictask/GwBME280.cpp @@ -21,7 +21,7 @@ #define PRFX2 "BME28012" #define PRFX3 "BME28021" #define PRFX4 "BME28022" -class BME280Config : public SensorBase{ +class BME280Config : public IICSensorBase{ public: bool prAct=true; bool tmAct=true; @@ -138,7 +138,7 @@ class BME280Config : public SensorBase{ }; -void registerBME280(GwApi *api,SensorList &sensors){ +void registerBME280(GwApi *api,IICSensorList &sensors){ #if defined(GWBME280) || defined(GWBME28011) { BME280Config *cfg=new BME280Config(api,PRFX1); @@ -173,7 +173,7 @@ void registerBME280(GwApi *api,SensorList &sensors){ #endif } #else -void registerBME280(GwApi *api,SensorList &sensors){ +void registerBME280(GwApi *api,IICSensorList &sensors){ } #endif diff --git a/lib/iictask/GwBME280.h b/lib/iictask/GwBME280.h index 03bfe57..5f2abb5 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); +void 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..51f787c 100644 --- a/lib/iictask/GwIicSensors.h +++ b/lib/iictask/GwIicSensors.h @@ -4,12 +4,16 @@ #include "N2kMessages.h" #include "GwXdrTypeMappings.h" #include "GwHardware.h" +#include "GwSensor.h" #ifdef _GWIIC #include #else class TwoWire; #endif +using BusType=TwoWire; +using IICSensorList=SensorList; +using IICSensorBase=SensorBase; #define CFG_GET(name,prefix) \ cfg->getValue(name, GwConfigDefinitions::prefix ## name) @@ -100,33 +104,6 @@ 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) diff --git a/lib/iictask/GwIicTask.cpp b/lib/iictask/GwIicTask.cpp index 51f3633..9698592 100644 --- a/lib/iictask/GwIicTask.cpp +++ b/lib/iictask/GwIicTask.cpp @@ -26,7 +26,7 @@ void runIicTask(GwApi *api); -static SensorList sensors; +static IICSensorList sensors; void initIicTask(GwApi *api){ GwLog *logger=api->getLogger(); @@ -124,7 +124,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()){ diff --git a/lib/iictask/GwQMP6988.cpp b/lib/iictask/GwQMP6988.cpp index 1f83199..638bf0e 100644 --- a/lib/iictask/GwQMP6988.cpp +++ b/lib/iictask/GwQMP6988.cpp @@ -4,7 +4,7 @@ #define PRFX2 "QMP698812" #define PRFX3 "QMP698821" #define PRFX4 "QMP698822" -class QMP6988Config : public SensorBase{ +class QMP6988Config : public IICSensorBase{ public: String prNam="Pressure"; bool prAct=true; @@ -76,7 +76,7 @@ class QMP6988Config : public SensorBase{ } }; -void registerQMP6988(GwApi *api,SensorList &sensors){ +void registerQMP6988(GwApi *api,IICSensorList &sensors){ GwLog *logger=api->getLogger(); #if defined(GWQMP6988) || defined(GWQMP698811) { @@ -113,5 +113,5 @@ void registerQMP6988(GwApi *api,SensorList &sensors){ } #else - void registerQMP6988(GwApi *api,SensorList &sensors){} + void registerQMP6988(GwApi *api,IICSensorList &sensors){} #endif \ No newline at end of file diff --git a/lib/iictask/GwQMP6988.h b/lib/iictask/GwQMP6988.h index 43e6426..5d64454 100644 --- a/lib/iictask/GwQMP6988.h +++ b/lib/iictask/GwQMP6988.h @@ -18,5 +18,5 @@ #ifdef _GWQMP6988 #include "QMP6988.h" #endif -void registerQMP6988(GwApi *api,SensorList &sensors); +void registerQMP6988(GwApi *api,IICSensorList &sensors); #endif \ No newline at end of file diff --git a/lib/iictask/GwSHT3X.cpp b/lib/iictask/GwSHT3X.cpp index e98647c..8305220 100644 --- a/lib/iictask/GwSHT3X.cpp +++ b/lib/iictask/GwSHT3X.cpp @@ -5,7 +5,7 @@ #define PRFX2 "SHT3X12" #define PRFX3 "SHT3X21" #define PRFX4 "SHT3X22" -class SHT3XConfig : public SensorBase{ +class SHT3XConfig : public IICSensorBase{ public: String tmNam; String huNam; @@ -104,7 +104,7 @@ class SHT3XConfig : public SensorBase{ intv*=1000; } }; -void registerSHT3X(GwApi *api,SensorList &sensors){ +void registerSHT3X(GwApi *api,IICSensorList &sensors){ GwLog *logger=api->getLogger(); #if defined(GWSHT3X) || defined (GWSHT3X11) { @@ -140,7 +140,7 @@ void registerSHT3X(GwApi *api,SensorList &sensors){ #endif } #else -void registerSHT3X(GwApi *api,SensorList &sensors){ +void registerSHT3X(GwApi *api,IICSensorList &sensors){ } diff --git a/lib/iictask/GwSHT3X.h b/lib/iictask/GwSHT3X.h index 085cab7..263d23e 100644 --- a/lib/iictask/GwSHT3X.h +++ b/lib/iictask/GwSHT3X.h @@ -18,5 +18,5 @@ #ifdef _GWSHT3X #include "SHT3X.h" #endif -void registerSHT3X(GwApi *api,SensorList &sensors); +void registerSHT3X(GwApi *api,IICSensorList &sensors); #endif \ No newline at end of file diff --git a/lib/sensors/GwSensor.h b/lib/sensors/GwSensor.h new file mode 100644 index 0000000..4988dd8 --- /dev/null +++ b/lib/sensors/GwSensor.h @@ -0,0 +1,49 @@ +/* + (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 +class SensorBase{ + public: + using BusType=BUS; + 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,BUS *wire){return false;}; + virtual bool preinit(GwApi * api){return false;} + virtual void measure(GwApi * api,BUS *wire, int counterId){}; + virtual ~SensorBase(){} +}; + +template +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); + this->push_back(sensor); + } + using std::vector*>::vector; +}; +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index c862767..0c33a9a 100644 --- a/src/main.cpp +++ b/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 From 5356b783c37d20b85fc6bb9b55b5000e94948a0c Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 1 Mar 2024 20:36:29 +0100 Subject: [PATCH 03/35] intermediate: add SPI/SSI task and DMS22B sensor --- lib/hardware/GwHardware.h | 7 ++ lib/iictask/GwIicSensors.h | 3 +- lib/sensors/GwSensor.h | 5 +- lib/spitask/GWDMS22B.cpp | 62 +++++++++++++++++ lib/spitask/GWDMS22B.h | 22 ++++++ lib/spitask/GwSpiSensor.h | 136 ++++++++++++++++++++++++++++++++++++ lib/spitask/GwSpiTask.cpp | 139 +++++++++++++++++++++++++++++++++++++ lib/spitask/GwSpiTask.h | 20 ++++++ lib/spitask/config.json | 92 ++++++++++++++++++++++++ lib/spitask/platformio.ini | 14 ++++ 10 files changed, 497 insertions(+), 3 deletions(-) create mode 100644 lib/spitask/GWDMS22B.cpp create mode 100644 lib/spitask/GWDMS22B.h create mode 100644 lib/spitask/GwSpiSensor.h create mode 100644 lib/spitask/GwSpiTask.cpp create mode 100644 lib/spitask/GwSpiTask.h create mode 100644 lib/spitask/config.json create mode 100644 lib/spitask/platformio.ini diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index ae4e640..273f424 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -322,4 +322,11 @@ #define CFGMODE_ledBrightness GwConfigInterface::HIDDEN #endif +#ifdef GWSPI0_CLK + #define _GWSPI +#endif +#ifdef GWSPI1_CLK + #define _GWSPI +#endif + #endif diff --git a/lib/iictask/GwIicSensors.h b/lib/iictask/GwIicSensors.h index 51f787c..c3d21b4 100644 --- a/lib/iictask/GwIicSensors.h +++ b/lib/iictask/GwIicSensors.h @@ -14,8 +14,7 @@ using BusType=TwoWire; using IICSensorList=SensorList; using IICSensorBase=SensorBase; -#define CFG_GET(name,prefix) \ - cfg->getValue(name, GwConfigDefinitions::prefix ## name) + template bool addPressureXdr(GwApi *api, CFG &cfg) diff --git a/lib/sensors/GwSensor.h b/lib/sensors/GwSensor.h index 4988dd8..e33aef3 100644 --- a/lib/sensors/GwSensor.h +++ b/lib/sensors/GwSensor.h @@ -19,7 +19,6 @@ template class SensorBase{ public: - using BusType=BUS; int busId=0; int iid=99; //N2K instanceId int addr=-1; @@ -46,4 +45,8 @@ class SensorList : public std::vector*>{ } using std::vector*>::vector; }; + +#define CFG_GET(name,prefix) \ + cfg->getValue(name, GwConfigDefinitions::prefix ## name) + #endif \ No newline at end of file diff --git a/lib/spitask/GWDMS22B.cpp b/lib/spitask/GWDMS22B.cpp new file mode 100644 index 0000000..cd4fed8 --- /dev/null +++ b/lib/spitask/GWDMS22B.cpp @@ -0,0 +1,62 @@ +/* + (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" +#define PREFIX1 "DMS22B11" +class GWDMS22B : public SSISensor{ + uint32_t zero=2047; + public: + using SSISensor::SSISensor; + virtual bool preinit(GwApi * api){ + GwLog *logger=api->getLogger(); + LOG_DEBUG(GwLog::LOG,"DMS22B configured, prefix=%s",prefix.c_str()); + api->addCapability(prefix,"true"); + return true; + } + 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); + } + LOG_DEBUG(GwLog::LOG,"measure %s : %d",prefix.c_str(),value); + } + #define DMS22B(prefix)\ + CFG_GET(act,prefix); \ + CFG_GET(iid,prefix); \ + CFG_GET(intv,prefix); \ + CFG_GET(zero,prefix); + virtual void readConfig(GwConfigHandler *cfg){ + if (prefix == PREFIX1){ + DMS22B(DMS22B11); + busId=GWSPIHOST1; + bits=12; + clock=500000; + #ifdef GWDMS22B11_CS + cs=GWDMS22B11_CS; + #else + cs=-1; + #endif + } + //TODO: other + } +}; + +void registerDMS22B(GwApi *api,SpiSensorList &sensors){ + #ifdef GWDMS22B11 + GWDMS22B *dms=new GWDMS22B(api,PREFIX1); + sensors.add(dms); + #endif +} diff --git a/lib/spitask/GWDMS22B.h b/lib/spitask/GWDMS22B.h new file mode 100644 index 0000000..5ef9bb5 --- /dev/null +++ b/lib/spitask/GWDMS22B.h @@ -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 \ No newline at end of file diff --git a/lib/spitask/GwSpiSensor.h b/lib/spitask/GwSpiSensor.h new file mode 100644 index 0000000..76f9e64 --- /dev/null +++ b/lib/spitask/GwSpiSensor.h @@ -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 +*/ +#ifndef _GWSPISENSOR_H +#define _GWSPISENSOR_H +#include +#include "GwSensor.h" +#include + +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{ + std::unique_ptr device; + protected: + int bits=12; + int mask=0xffff; + int cs=-1; + int clock=0; + bool act=false; + 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: + using SensorBase::SensorBase; + virtual bool isActive(){return act;}; + virtual bool initDevice(GwApi *api,BusType *bus){ + return initSSI(api->getLogger(),bus, clock,cs,bits); + }; + +}; +using SpiSensorList=SensorList; +#define GWSPIHOST1 SPI2_HOST +#define GWSPIHOST2 SPI3_HOST +#endif \ No newline at end of file diff --git a/lib/spitask/GwSpiTask.cpp b/lib/spitask/GwSpiTask.cpp new file mode 100644 index 0000000..14e02a9 --- /dev/null +++ b/lib/spitask/GwSpiTask.cpp @@ -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 "GwSpiTask.h" +#include "GwSpiSensor.h" +#include "GWDMS22B.h" +#include "GwTimer.h" + +static SPIBus bus1(GWSPIHOST1); +static SPIBus bus2(GWSPIHOST2); + +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 + +#define _GWSPI +void runSpiTask(GwApi *api){ + GwLog *logger=api->getLogger(); + std::map buses; + for (auto && sensor:sensors){ + int busId=sensor->busId; + auto bus=buses.find(busId); + if (bus == buses.end()){ + switch (busId) + { + case 1: + if (spi1clk < 0){ + LOG_DEBUG(GwLog::ERROR,"SPI bus 1 not configured, cannot create %s",sensor->prefix.c_str()); + } + else{ + if (bus1.init(logger,spi1miso,spi1mosi,spi1clk)){ + buses[busId]=&bus1; + } + } + break; + case 2: + if (spi2clk < 0){ + LOG_DEBUG(GwLog::ERROR,"SPI bus 2 not configured, cannot create %s",sensor->prefix.c_str()); + } + else{ + if (bus2.init(logger,spi2miso,spi2mosi,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(); + #ifndef _GWSPI + return; + #endif + 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"); + } +} \ No newline at end of file diff --git a/lib/spitask/GwSpiTask.h b/lib/spitask/GwSpiTask.h new file mode 100644 index 0000000..ddcac8b --- /dev/null +++ b/lib/spitask/GwSpiTask.h @@ -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 \ No newline at end of file diff --git a/lib/spitask/config.json b/lib/spitask/config.json new file mode 100644 index 0000000..4c9d13c --- /dev/null +++ b/lib/spitask/config.json @@ -0,0 +1,92 @@ +[ + { + "type": "array", + "name": "DMS22B", + "replace": [ + { + "b": "1", + "i": "11", + "n": "11" + }, + { + "b": "1", + "i": "12", + "n": "12" + }, + { + "b": "1", + "i": "13", + "n": "13" + }, + { + "b": "2", + "i": "21", + "n": "21" + }, + { + "b": "2", + "i": "22", + "n": "22" + }, + { + "b": "2", + "i": "23", + "n": "23" + } + + + ], + "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$iintv", + "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": "number", + "default": 2, + "description": "Zero position (0...2^bits-1)", + "category": "spisensors$b", + "capabilities": { + "DMS22B$i": "true" + } + } + ] + } +] \ No newline at end of file diff --git a/lib/spitask/platformio.ini b/lib/spitask/platformio.ini new file mode 100644 index 0000000..d0c8f21 --- /dev/null +++ b/lib/spitask/platformio.ini @@ -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} From 0c94293ccb69cd5aed800016207f79c261d1c675 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 2 Mar 2024 17:51:23 +0100 Subject: [PATCH 04/35] working spi task, measure with dms22b --- lib/config/GWConfig.h | 9 +++ lib/config/GwConfigItem.h | 3 + lib/spitask/GWDMS22B.cpp | 124 ++++++++++++++++++++++++++++++------- lib/spitask/GwSpiSensor.h | 10 ++- lib/spitask/GwSpiTask.cpp | 48 +++++++------- lib/spitask/config.json | 2 +- lib/spitask/platformio.ini | 4 +- 7 files changed, 147 insertions(+), 53 deletions(-) diff --git a/lib/config/GWConfig.h b/lib/config/GWConfig.h index 521d7a2..6264506 100644 --- a/lib/config/GWConfig.h +++ b/lib/config/GWConfig.h @@ -70,6 +70,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..d739902 100644 --- a/lib/config/GwConfigItem.h +++ b/lib/config/GwConfigItem.h @@ -37,6 +37,9 @@ class GwConfigInterface{ virtual int asInt() const{ return (int)value.toInt(); } + virtual float asFloat() const{ + return value.toFloat(); + } String getName() const{ return name; } diff --git a/lib/spitask/GWDMS22B.cpp b/lib/spitask/GWDMS22B.cpp index cd4fed8..4f0daf7 100644 --- a/lib/spitask/GWDMS22B.cpp +++ b/lib/spitask/GWDMS22B.cpp @@ -13,14 +13,86 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "GWDMS22B.h" -#define PREFIX1 "DMS22B11" +#include "GwApi.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,SPI0) + #ifndef GWDMS22B11_CS + #define GWDMS22B11_CS -1 + #endif +#else + #define GWDMS22B11_CS -1 + #define ADD22B11 +#endif + +#ifdef GWDMS22B12 + #define ADD22B12 ADD22B(DMS22B12,SPI0) + #ifndef GWDMS22B12_CS + #define GWDMS22B12_CS -1 + #endif +#else + #define GWDMS22B12_CS -1 + #define ADD22B12 +#endif + +#ifdef GWDMS22B13 + #define ADD22B13 ADD22B(DMS22B13,SPI0) + #ifndef GWDMS22B13_CS + #define GWDMS22B13_CS -1 + #endif +#else + #define GWDMS22B13_CS -1 + #define ADD22B13 +#endif + +#ifdef GWDMS22B21 + #define ADD22B21 ADD22B(DMS22B21,SPI1) + #ifndef GWDMS22B21_CS + #define GWDMS22B21_CS -1 + #endif +#else + #define GWDMS22B21_CS -1 + #define ADD22B21 +#endif + +#ifdef GWDMS22B22 + #define ADD22B22 ADD22B(DMS22B22,SPI1) + #ifndef GWDMS22B22_CS + #define GWDMS22B22_CS -1 + #endif +#else + #define GWDMS22B22_CS -1 + #define ADD22B22 +#endif + +#ifdef GWDMS22B23 + #define ADD22B23 ADD22B(DMS22B23,SPI1) + #ifndef GWDMS22B23_CS + #define GWDMS22B23_CS -1 + #endif +#else + #define GWDMS22B23_CS -1 + #define ADD22B23 +#endif + + + class GWDMS22B : public SSISensor{ uint32_t zero=2047; public: using SSISensor::SSISensor; virtual bool preinit(GwApi * api){ GwLog *logger=api->getLogger(); - LOG_DEBUG(GwLog::LOG,"DMS22B configured, prefix=%s",prefix.c_str()); + LOG_DEBUG(GwLog::LOG,"DMS22B configured, prefix=%s, intv=%f",prefix.c_str()); api->addCapability(prefix,"true"); return true; } @@ -33,30 +105,36 @@ class GWDMS22B : public SSISensor{ } LOG_DEBUG(GwLog::LOG,"measure %s : %d",prefix.c_str(),value); } - #define DMS22B(prefix)\ - CFG_GET(act,prefix); \ - CFG_GET(iid,prefix); \ - CFG_GET(intv,prefix); \ - CFG_GET(zero,prefix); - virtual void readConfig(GwConfigHandler *cfg){ - if (prefix == PREFIX1){ - DMS22B(DMS22B11); - busId=GWSPIHOST1; - bits=12; - clock=500000; - #ifdef GWDMS22B11_CS - cs=GWDMS22B11_CS; - #else - cs=-1; - #endif + #define DMS22B(PRFX,...) \ + if (prefix == #PRFX) {\ + CFG_GET(act,PRFX); \ + CFG_GET(iid,PRFX); \ + CFG_GET(fintv,PRFX); \ + CFG_GET(zero,PRFX); \ + bits=12; \ + clock=500000; \ + cs=GW ## PRFX ## _CS; \ + __VA_ARGS__ \ } - //TODO: other + + + virtual void readConfig(GwConfigHandler *cfg){ + DMS22B(DMS22B11); + DMS22B(DMS22B12); + DMS22B(DMS22B13); + DMS22B(DMS22B21); + DMS22B(DMS22B22); + DMS22B(DMS22B23); + intv=1000*fintv; } }; void registerDMS22B(GwApi *api,SpiSensorList &sensors){ - #ifdef GWDMS22B11 - GWDMS22B *dms=new GWDMS22B(api,PREFIX1); - sensors.add(dms); - #endif + ADD22B11 + ADD22B12 + ADD22B13 + ADD22B21 + ADD22B22 + ADD22B23 + } diff --git a/lib/spitask/GwSpiSensor.h b/lib/spitask/GwSpiSensor.h index 76f9e64..3b42594 100644 --- a/lib/spitask/GwSpiSensor.h +++ b/lib/spitask/GwSpiSensor.h @@ -97,6 +97,7 @@ class SSISensor : public SensorBase{ 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; @@ -123,7 +124,10 @@ class SSISensor : public SensorBase{ } public: - using SensorBase::SensorBase; + SSISensor(GwApi *api,const String &prfx, int host):SensorBase(api,prfx) + { + busId=host; + } virtual bool isActive(){return act;}; virtual bool initDevice(GwApi *api,BusType *bus){ return initSSI(api->getLogger(),bus, clock,cs,bits); @@ -131,6 +135,6 @@ class SSISensor : public SensorBase{ }; using SpiSensorList=SensorList; -#define GWSPIHOST1 SPI2_HOST -#define GWSPIHOST2 SPI3_HOST +#define GWSPI0_HOST SPI2_HOST +#define GWSPI1_HOST SPI3_HOST #endif \ No newline at end of file diff --git a/lib/spitask/GwSpiTask.cpp b/lib/spitask/GwSpiTask.cpp index 14e02a9..084cece 100644 --- a/lib/spitask/GwSpiTask.cpp +++ b/lib/spitask/GwSpiTask.cpp @@ -17,11 +17,27 @@ #include "GWDMS22B.h" #include "GwTimer.h" -static SPIBus bus1(GWSPIHOST1); -static SPIBus bus2(GWSPIHOST2); +static SPIBus bus1(GWSPI0_HOST); +static SPIBus bus2(GWSPI1_HOST); static SpiSensorList sensors; +#ifdef GWSPI0_CLK +static const int spi0clk=GWSPI0_CLK; +#else +static const int spi0clk=-1; +#endif +#ifdef GWSPI0_MISO +static const int spi0miso=GWSPI0_MISO; +#else +static const int spi0miso=-1; +#endif +#ifdef GWSPI0_MOSI +static const int spi0mosi=GWSPI0_MOSI; +#else +static const int spi0mosi=-1; +#endif + #ifdef GWSPI1_CLK static const int spi1clk=GWSPI1_CLK; #else @@ -38,22 +54,6 @@ static const int spi1mosi=GWSPI1_MOSI; 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 - #define _GWSPI void runSpiTask(GwApi *api){ GwLog *logger=api->getLogger(); @@ -64,22 +64,22 @@ void runSpiTask(GwApi *api){ if (bus == buses.end()){ switch (busId) { - case 1: - if (spi1clk < 0){ + case GWSPI0_HOST: + if (spi0clk < 0){ LOG_DEBUG(GwLog::ERROR,"SPI bus 1 not configured, cannot create %s",sensor->prefix.c_str()); } else{ - if (bus1.init(logger,spi1miso,spi1mosi,spi1clk)){ + if (bus1.init(logger,spi0mosi,spi0miso,spi0clk)){ buses[busId]=&bus1; } } break; - case 2: - if (spi2clk < 0){ + case GWSPI1_HOST: + if (spi1clk < 0){ LOG_DEBUG(GwLog::ERROR,"SPI bus 2 not configured, cannot create %s",sensor->prefix.c_str()); } else{ - if (bus2.init(logger,spi2miso,spi2mosi,spi2clk)){ + if (bus2.init(logger,spi1mosi,spi1miso,spi1clk)){ buses[busId]=&bus2; } } diff --git a/lib/spitask/config.json b/lib/spitask/config.json index 4c9d13c..720e8b5 100644 --- a/lib/spitask/config.json +++ b/lib/spitask/config.json @@ -63,7 +63,7 @@ } }, { - "name": "DMS22B$iintv", + "name": "DMS22B$ifintv", "label": "DMS22B$i Interval", "type": "number", "default": 2, diff --git a/lib/spitask/platformio.ini b/lib/spitask/platformio.ini index d0c8f21..a6d5737 100644 --- a/lib/spitask/platformio.ini +++ b/lib/spitask/platformio.ini @@ -7,8 +7,8 @@ lib_deps = ${env.lib_deps} ${sensors.lib_deps} build_flags= - -D GWSPI1_CLK=21 - -D GWSPI1_MISO=25 + -D GWSPI0_CLK=21 + -D GWSPI0_MISO=25 -D GWDMS22B11 -D GWDMS22B11_CS=22 ${env.build_flags} From ebd4a8907afe40d35ebed3356b5bc047406e0030 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 2 Mar 2024 18:36:58 +0100 Subject: [PATCH 05/35] send n2k message for rudder angle --- lib/spitask/GWDMS22B.cpp | 11 ++++++++--- lib/spitask/config.json | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/spitask/GWDMS22B.cpp b/lib/spitask/GWDMS22B.cpp index 4f0daf7..51ad88c 100644 --- a/lib/spitask/GWDMS22B.cpp +++ b/lib/spitask/GWDMS22B.cpp @@ -14,6 +14,7 @@ */ #include "GWDMS22B.h" #include "GwApi.h" +#include "N2kMessages.h" #define CHECK_BUS(BUS) \ @@ -87,12 +88,12 @@ class GWDMS22B : public SSISensor{ - uint32_t zero=2047; + int zero=2047; public: using SSISensor::SSISensor; virtual bool preinit(GwApi * api){ GwLog *logger=api->getLogger(); - LOG_DEBUG(GwLog::LOG,"DMS22B configured, prefix=%s, intv=%f",prefix.c_str()); + LOG_DEBUG(GwLog::LOG,"DMS22B configured, prefix=%s, intv=%f",prefix.c_str(),fintv); api->addCapability(prefix,"true"); return true; } @@ -103,7 +104,11 @@ class GWDMS22B : public SSISensor{ if (res != ESP_OK){ LOG_DEBUG(GwLog::ERROR,"unable to measure %s: %d",prefix.c_str(),(int)res); } - LOG_DEBUG(GwLog::LOG,"measure %s : %d",prefix.c_str(),value); + double resolved=(((int)value-zero)*360.0/mask); + LOG_DEBUG(GwLog::LOG,"measure %s : %d, resolved: %f",prefix.c_str(),value,(float)resolved); + tN2kMsg msg; + SetN2kRudder(msg,DegToRad(resolved),iid); + api->sendN2kMessage(msg); } #define DMS22B(PRFX,...) \ if (prefix == #PRFX) {\ diff --git a/lib/spitask/config.json b/lib/spitask/config.json index 720e8b5..8827fca 100644 --- a/lib/spitask/config.json +++ b/lib/spitask/config.json @@ -80,7 +80,7 @@ "name": "DMS22B$izero", "label": "DMS22B$i Zero", "type": "number", - "default": 2, + "default": 2048, "description": "Zero position (0...2^bits-1)", "category": "spisensors$b", "capabilities": { From 71f0bfa6f3599a09483d2cc9d96e9c99f571ca6f Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 2 Mar 2024 19:59:00 +0100 Subject: [PATCH 06/35] allow to convert 2 rudder instances --- lib/boatData/GwBoatData.h | 3 +- lib/nmea2kto0183/N2kDataToNMEA0183.cpp | 45 +++++++++++++++----------- lib/nmea2kto0183/N2kDataToNMEA0183.h | 10 ++++-- lib/spitask/GWDMS22B.cpp | 5 ++- lib/spitask/GwSpiSensor.h | 1 + lib/spitask/config.json | 15 +++++++-- src/main.cpp | 7 ++-- web/config.json | 22 +++++++++++++ 8 files changed, 82 insertions(+), 26 deletions(-) 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/nmea2kto0183/N2kDataToNMEA0183.cpp b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp index 1f490df..e8b150f 100644 --- a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp +++ b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp @@ -62,7 +62,6 @@ class N2kToNMEA0183Functions : public N2kDataToNMEA0183 { private: - int minXdrInterval=100; //minimal interval between 2 sends of the same transducer GwXDRMappings *xdrMappings; ConverterList converters; std::map lastSendTransducers; @@ -75,7 +74,7 @@ private: 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){ @@ -611,24 +610,34 @@ private: if (ParseN2kRudder(N2kMsg, RudderPosition, Instance, RudderDirectionOrder, AngleOrder)) { - - updateDouble(boatData->RPOS, RudderPosition); - if (Instance != 0) + bool main=false; + if (Instance == config.starboardRudderInstance){ + updateDouble(boatData->RPOS, RudderPosition); + main=true; + } + 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; - + if (main){ + if (!NMEA0183Msg.AddDoubleField(formatWind(RudderPosition)))return; + if (!NMEA0183Msg.AddStrField("A"))return; + if (!NMEA0183Msg.AddDoubleField(0.0))return; + if (!NMEA0183Msg.AddStrField("V"))return; + } + else{ + if (!NMEA0183Msg.AddDoubleField(0.0))return; + if (!NMEA0183Msg.AddStrField("V"))return; + if (!NMEA0183Msg.AddDoubleField(formatWind(RudderPosition)))return; + if (!NMEA0183Msg.AddStrField("A"))return; + } SendMessage(NMEA0183Msg); } } @@ -1508,7 +1517,7 @@ private: public: N2kToNMEA0183Functions(GwLog *logger, GwBoatData *boatData, SendNMEA0183MessageCallback callback, - String talkerId, GwXDRMappings *xdrMappings, int minXdrInterval) + String talkerId, GwXDRMappings *xdrMappings, const Config &cfg) : N2kDataToNMEA0183(logger, boatData, callback,talkerId) { LastPosSend = 0; @@ -1518,7 +1527,7 @@ private: this->logger = logger; this->boatData = boatData; this->xdrMappings=xdrMappings; - this->minXdrInterval=minXdrInterval; + this->config=cfg; registerConverters(); } virtual void loop() @@ -1535,8 +1544,8 @@ private: N2kDataToNMEA0183* N2kDataToNMEA0183::create(GwLog *logger, GwBoatData *boatData, SendNMEA0183MessageCallback callback, String talkerId, GwXDRMappings *xdrMappings, - int minXdrInterval){ + const N2kDataToNMEA0183::Config &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); } //***************************************************************************** diff --git a/lib/nmea2kto0183/N2kDataToNMEA0183.h b/lib/nmea2kto0183/N2kDataToNMEA0183.h index a317cdd..ec37429 100644 --- a/lib/nmea2kto0183/N2kDataToNMEA0183.h +++ b/lib/nmea2kto0183/N2kDataToNMEA0183.h @@ -36,8 +36,14 @@ class N2kDataToNMEA0183 { public: typedef std::function SendNMEA0183MessageCallback; - + class Config{ + public: + int minXdrInterval=100; + int starboardRudderInstance=0; + int portRudderInstance=-1; //ignore + }; protected: + Config config; GwLog *logger; GwBoatData *boatData; int sourceId=0; @@ -49,7 +55,7 @@ protected: public: static N2kDataToNMEA0183* create(GwLog *logger, GwBoatData *boatData, SendNMEA0183MessageCallback callback, - String talkerId, GwXDRMappings *xdrMappings,int minXdrInterval=100); + String talkerId, GwXDRMappings *xdrMappings,const Config &cfg); virtual void HandleMsg(const tN2kMsg &N2kMsg, int sourceId) = 0; virtual void loop(); virtual ~N2kDataToNMEA0183(){} diff --git a/lib/spitask/GWDMS22B.cpp b/lib/spitask/GWDMS22B.cpp index 51ad88c..2327415 100644 --- a/lib/spitask/GWDMS22B.cpp +++ b/lib/spitask/GWDMS22B.cpp @@ -89,6 +89,7 @@ class GWDMS22B : public SSISensor{ int zero=2047; + bool invt=false; public: using SSISensor::SSISensor; virtual bool preinit(GwApi * api){ @@ -105,7 +106,8 @@ class GWDMS22B : public SSISensor{ LOG_DEBUG(GwLog::ERROR,"unable to measure %s: %d",prefix.c_str(),(int)res); } double resolved=(((int)value-zero)*360.0/mask); - LOG_DEBUG(GwLog::LOG,"measure %s : %d, resolved: %f",prefix.c_str(),value,(float)resolved); + 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); @@ -116,6 +118,7 @@ class GWDMS22B : public SSISensor{ CFG_GET(iid,PRFX); \ CFG_GET(fintv,PRFX); \ CFG_GET(zero,PRFX); \ + CFG_GET(invt,PRFX); \ bits=12; \ clock=500000; \ cs=GW ## PRFX ## _CS; \ diff --git a/lib/spitask/GwSpiSensor.h b/lib/spitask/GwSpiSensor.h index 3b42594..48e4a78 100644 --- a/lib/spitask/GwSpiSensor.h +++ b/lib/spitask/GwSpiSensor.h @@ -12,6 +12,7 @@ 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 diff --git a/lib/spitask/config.json b/lib/spitask/config.json index 8827fca..3beabeb 100644 --- a/lib/spitask/config.json +++ b/lib/spitask/config.json @@ -6,12 +6,12 @@ { "b": "1", "i": "11", - "n": "11" + "n": "0" }, { "b": "1", "i": "12", - "n": "12" + "n": "1" }, { "b": "1", @@ -86,6 +86,17 @@ "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" + } } ] } diff --git a/src/main.cpp b/src/main.cpp index 0c33a9a..30026c0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -808,7 +808,10 @@ void setup() { webserver.begin(); xdrMappings.begin(); logger.flush(); - + N2kDataToNMEA0183::Config n2kTo183cfg; + n2kTo183cfg.minXdrInterval=config.getInt(config.minXdrInterval,100); + n2kTo183cfg.starboardRudderInstance=config.getInt(config.stbRudderI,0); + n2kTo183cfg.portRudderInstance=config.getInt(config.portRudderI,-1); nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData, [](const tNMEA0183Msg &msg, int sourceId){ SendNMEA0183Message(msg,sourceId,false); @@ -816,7 +819,7 @@ void setup() { , config.getString(config.talkerId,String("GP")), &xdrMappings, - config.getInt(config.minXdrInterval,100) + n2kTo183cfg ); toN2KConverter= NMEA0183DataToN2K::create(&logger,&boatData,[](const tN2kMsg &msg, int sourceId)->bool{ diff --git a/web/config.json b/web/config.json index 8ad556f..2b74c0d 100644 --- a/web/config.json +++ b/web/config.json @@ -210,6 +210,28 @@ "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": "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", From c7c5f6c9b9a4b3f6e227d91b2a67d0bd887aaeb1 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 3 Mar 2024 16:36:49 +0100 Subject: [PATCH 07/35] better status handling for multiple SSI devices --- lib/spitask/GWDMS22B.cpp | 24 +++++++++++++++++++----- lib/spitask/config.json | 2 +- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/spitask/GWDMS22B.cpp b/lib/spitask/GWDMS22B.cpp index 2327415..d386bd8 100644 --- a/lib/spitask/GWDMS22B.cpp +++ b/lib/spitask/GWDMS22B.cpp @@ -21,9 +21,11 @@ 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); + sensors.add(api,dms); \ + } #ifdef GWDMS22B11 #define ADD22B11 ADD22B(DMS22B11,SPI0) @@ -38,7 +40,10 @@ #ifdef GWDMS22B12 #define ADD22B12 ADD22B(DMS22B12,SPI0) #ifndef GWDMS22B12_CS - #define GWDMS22B12_CS -1 + #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 @@ -48,7 +53,10 @@ #ifdef GWDMS22B13 #define ADD22B13 ADD22B(DMS22B13,SPI0) #ifndef GWDMS22B13_CS - #define GWDMS22B13_CS -1 + #error "you need to define GWDMS22B13_CS" + #endif + #if GWDMS22B11_CS == -1 + #error "multiple devices on one SPI bus need chip select defines - GWDMS22B11_CS is unset" #endif #else #define GWDMS22B13_CS -1 @@ -68,7 +76,10 @@ #ifdef GWDMS22B22 #define ADD22B22 ADD22B(DMS22B22,SPI1) #ifndef GWDMS22B22_CS - #define GWDMS22B22_CS -1 + #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 @@ -78,7 +89,10 @@ #ifdef GWDMS22B23 #define ADD22B23 ADD22B(DMS22B23,SPI1) #ifndef GWDMS22B23_CS - #define GWDMS22B23_CS -1 + #error "you need to define GWDMS22B23_CS" + #endif + #if GWDMS22B21_CS == -1 + #error "multiple devices on one SPI bus need chip select defines - GWDMS22B11_CS is unset" #endif #else #define GWDMS22B23_CS -1 diff --git a/lib/spitask/config.json b/lib/spitask/config.json index 3beabeb..8b1ece2 100644 --- a/lib/spitask/config.json +++ b/lib/spitask/config.json @@ -21,7 +21,7 @@ { "b": "2", "i": "21", - "n": "21" + "n": "1" }, { "b": "2", From b327d9197651f04749fa8377f43b3ebc3a99c7ef Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 3 Mar 2024 16:48:11 +0100 Subject: [PATCH 08/35] better handling of defined/enabled spi sensors --- lib/hardware/GwHardware.h | 6 ------ lib/spitask/GWDMS22B.cpp | 4 ++-- lib/spitask/GwSpiTask.cpp | 6 +----- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 273f424..18d6084 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -322,11 +322,5 @@ #define CFGMODE_ledBrightness GwConfigInterface::HIDDEN #endif -#ifdef GWSPI0_CLK - #define _GWSPI -#endif -#ifdef GWSPI1_CLK - #define _GWSPI -#endif #endif diff --git a/lib/spitask/GWDMS22B.cpp b/lib/spitask/GWDMS22B.cpp index d386bd8..fcd51f1 100644 --- a/lib/spitask/GWDMS22B.cpp +++ b/lib/spitask/GWDMS22B.cpp @@ -108,9 +108,9 @@ class GWDMS22B : public SSISensor{ using SSISensor::SSISensor; virtual bool preinit(GwApi * api){ GwLog *logger=api->getLogger(); - LOG_DEBUG(GwLog::LOG,"DMS22B configured, prefix=%s, intv=%f",prefix.c_str(),fintv); + LOG_DEBUG(GwLog::LOG,"DMS22B configured, prefix=%s, intv=%f, active=%d",prefix.c_str(),fintv,(int)act); api->addCapability(prefix,"true"); - return true; + return act; } virtual void measure(GwApi * api,BusType *bus, int counterId){ GwLog *logger=api->getLogger(); diff --git a/lib/spitask/GwSpiTask.cpp b/lib/spitask/GwSpiTask.cpp index 084cece..daa34b0 100644 --- a/lib/spitask/GwSpiTask.cpp +++ b/lib/spitask/GwSpiTask.cpp @@ -54,7 +54,6 @@ static const int spi1mosi=GWSPI1_MOSI; static const int spi1mosi=-1; #endif -#define _GWSPI void runSpiTask(GwApi *api){ GwLog *logger=api->getLogger(); std::map buses; @@ -122,9 +121,6 @@ void runSpiTask(GwApi *api){ void initSpiTask(GwApi *api){ GwLog *logger=api->getLogger(); - #ifndef _GWSPI - return; - #endif registerDMS22B(api,sensors); bool addTask=false; for (auto && sensor:sensors){ @@ -134,6 +130,6 @@ void initSpiTask(GwApi *api){ api->addUserTask(runSpiTask,"spiTask",3000); } else{ - LOG_DEBUG(GwLog::LOG,"no SPI sensors defined"); + LOG_DEBUG(GwLog::LOG,"no SPI sensors defined/active"); } } \ No newline at end of file From e10432dae4b574858b71584c6cd0775e8c7b2c00 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 3 Mar 2024 18:37:20 +0100 Subject: [PATCH 09/35] correctly show source names --- web/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/index.js b/web/index.js index 31ec3f9..bc5bcdf 100644 --- a/web/index.js +++ b/web/index.js @@ -1506,7 +1506,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; } From 47a2eb52f5c096011c2c3b426e17ec5ecd8ea0cd Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 3 Mar 2024 18:37:38 +0100 Subject: [PATCH 10/35] allow to configure sendRMC --- lib/config/GwConverterConfig.h | 41 ++++++ lib/nmea0183ton2k/NMEA0183DataToN2K.cpp | 40 ++++-- lib/nmea0183ton2k/NMEA0183DataToN2K.h | 6 +- lib/nmea2kto0183/N2kDataToNMEA0183.cpp | 52 +++---- lib/nmea2kto0183/N2kDataToNMEA0183.h | 19 +-- src/main.cpp | 13 +- web/config.json | 180 +++++++++++++----------- 7 files changed, 206 insertions(+), 145 deletions(-) create mode 100644 lib/config/GwConverterConfig.h 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/nmea0183ton2k/NMEA0183DataToN2K.cpp b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp index 9d66e08..bfed8fd 100644 --- a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp +++ b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp @@ -29,7 +29,6 @@ private: MyAisDecoder *aisDecoder=NULL; ConverterList converters; std::map 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 *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; } + tN2kMsg n2kMsg; 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.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); } diff --git a/lib/nmea0183ton2k/NMEA0183DataToN2K.h b/lib/nmea0183ton2k/NMEA0183DataToN2K.h index d56b80a..f01d648 100644 --- a/lib/nmea0183ton2k/NMEA0183DataToN2K.h +++ b/lib/nmea0183ton2k/NMEA0183DataToN2K.h @@ -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 \ No newline at end of file diff --git a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp index e8b150f..26c6811 100644 --- a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp +++ b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp @@ -43,7 +43,7 @@ N2kDataToNMEA0183::N2kDataToNMEA0183(GwLog * logger, GwBoatData *boatData, //***************************************************************************** -void N2kDataToNMEA0183::loop() { +void N2kDataToNMEA0183::loop(unsigned long) { } //***************************************************************************** @@ -65,10 +65,10 @@ private: GwXDRMappings *xdrMappings; ConverterList converters; std::map 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); @@ -133,9 +133,6 @@ private: return boatData->update((double)value,sourceId,mapping); } - unsigned long LastPosSend; - unsigned long NextRMCSend; - unsigned long lastLoopTime; virtual unsigned long *handledPgns() { @@ -165,7 +162,6 @@ private: { return converters.numConverters(); } - void SetNextRMCSend() { NextRMCSend = millis() + RMCPeriod; } //*************** the converters *********************** void HandleHeading(const tN2kMsg &N2kMsg) @@ -545,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, @@ -564,7 +558,6 @@ private: { SendMessage(NMEA0183Msg); } - SetNextRMCSend(); } } @@ -610,10 +603,8 @@ private: if (ParseN2kRudder(N2kMsg, RudderPosition, Instance, RudderDirectionOrder, AngleOrder)) { - bool main=false; if (Instance == config.starboardRudderInstance){ updateDouble(boatData->RPOS, RudderPosition); - main=true; } else if (Instance == config.portRudderInstance){ updateDouble(boatData->PRPOS, RudderPosition); @@ -626,18 +617,24 @@ private: if (!NMEA0183Msg.Init("RSA", talkerId)) return; - if (main){ - if (!NMEA0183Msg.AddDoubleField(formatWind(RudderPosition)))return; + auto rpos=boatData->RPOS; + if (rpos->isValid()){ + if (!NMEA0183Msg.AddDoubleField(formatWind(rpos->getData())))return; if (!NMEA0183Msg.AddStrField("A"))return; - if (!NMEA0183Msg.AddDoubleField(0.0))return; - if (!NMEA0183Msg.AddStrField("V"))return; } else{ if (!NMEA0183Msg.AddDoubleField(0.0))return; if (!NMEA0183Msg.AddStrField("V"))return; - if (!NMEA0183Msg.AddDoubleField(formatWind(RudderPosition)))return; + } + auto prpos=boatData->PRPOS; + if (prpos->isValid()){ + if (!NMEA0183Msg.AddDoubleField(formatWind(prpos->getData())))return; if (!NMEA0183Msg.AddStrField("A"))return; } + else{ + if (!NMEA0183Msg.AddDoubleField(0.0))return; + if (!NMEA0183Msg.AddStrField("V"))return; + } SendMessage(NMEA0183Msg); } } @@ -1517,34 +1514,29 @@ private: public: N2kToNMEA0183Functions(GwLog *logger, GwBoatData *boatData, SendNMEA0183MessageCallback callback, - String talkerId, GwXDRMappings *xdrMappings, const Config &cfg) + 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->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; - SendRMC(); + 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, - const N2kDataToNMEA0183::Config &cfg){ + const GwConverterConfig &cfg){ LOG_DEBUG(GwLog::LOG,"creating N2kToNMEA0183"); return new N2kToNMEA0183Functions(logger,boatData,callback, talkerId,xdrMappings,cfg); } diff --git a/lib/nmea2kto0183/N2kDataToNMEA0183.h b/lib/nmea2kto0183/N2kDataToNMEA0183.h index ec37429..61e8ee2 100644 --- a/lib/nmea2kto0183/N2kDataToNMEA0183.h +++ b/lib/nmea2kto0183/N2kDataToNMEA0183.h @@ -26,9 +26,10 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#include -#include -#include +#include "GwLog.h" +#include "GwBoatData.h" +#include "GwXDRMappings.h" +#include "GwConverterConfig.h" //------------------------------------------------------------------------------ class GwJsonDocument; @@ -36,14 +37,8 @@ class N2kDataToNMEA0183 { public: typedef std::function SendNMEA0183MessageCallback; - class Config{ - public: - int minXdrInterval=100; - int starboardRudderInstance=0; - int portRudderInstance=-1; //ignore - }; protected: - Config config; + GwConverterConfig config; GwLog *logger; GwBoatData *boatData; int sourceId=0; @@ -55,9 +50,9 @@ protected: public: static N2kDataToNMEA0183* create(GwLog *logger, GwBoatData *boatData, SendNMEA0183MessageCallback callback, - String talkerId, GwXDRMappings *xdrMappings,const Config &cfg); + String talkerId, GwXDRMappings *xdrMappings,const GwConverterConfig &cfg); virtual void HandleMsg(const tN2kMsg &N2kMsg, int sourceId) = 0; - virtual void loop(); + virtual void loop(unsigned long lastRmc); virtual ~N2kDataToNMEA0183(){} virtual unsigned long* handledPgns()=0; virtual int numPgns()=0; diff --git a/src/main.cpp b/src/main.cpp index 30026c0..5ce1b7a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -808,10 +808,8 @@ void setup() { webserver.begin(); xdrMappings.begin(); logger.flush(); - N2kDataToNMEA0183::Config n2kTo183cfg; - n2kTo183cfg.minXdrInterval=config.getInt(config.minXdrInterval,100); - n2kTo183cfg.starboardRudderInstance=config.getInt(config.stbRudderI,0); - n2kTo183cfg.portRudderInstance=config.getInt(config.portRudderI,-1); + GwConverterConfig converterConfig; + converterConfig.init(&config); nmea0183Converter= N2kDataToNMEA0183::create(&logger, &boatData, [](const tNMEA0183Msg &msg, int sourceId){ SendNMEA0183Message(msg,sourceId,false); @@ -819,7 +817,7 @@ void setup() { , config.getString(config.talkerId,String("GP")), &xdrMappings, - n2kTo183cfg + converterConfig ); toN2KConverter= NMEA0183DataToN2K::create(&logger,&boatData,[](const tN2kMsg &msg, int sourceId)->bool{ @@ -828,7 +826,7 @@ void setup() { return true; }, &xdrMappings, - config.getInt(config.min2KInterval,50) + converterConfig ); NMEA2000.SetN2kCANMsgBufSize(8); @@ -950,7 +948,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 diff --git a/web/config.json b/web/config.json index 2b74c0d..ebc5a27 100644 --- a/web/config.json +++ b/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,24 @@ "description":"send out the converted data on the NMEA2000 bus\nIf set to off the converted data will still be shown at the data tab.", "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", From ebf0824d5e7f0f0d9ab1bfa640ceca8ca1441018 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 3 Mar 2024 20:22:54 +0100 Subject: [PATCH 11/35] add m5 proto hub to build service --- lib/hardware/GwHardware.h | 18 ++++++++ webinstall/build.yaml | 96 +++++++++++++++++++++++++++------------ webinstall/cibuild.js | 18 ++++++-- 3 files changed, 99 insertions(+), 33 deletions(-) diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 18d6084..6680e3c 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -42,6 +42,10 @@ #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 @@ -59,6 +63,10 @@ #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 @@ -167,6 +175,16 @@ #define CFGMODE_serialBaud GwConfigInterface::READONLY #endif +//M5 ProtoHub +#ifdef 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 + //below we define the final device config based on the above //boards and peripherals //this allows us to easily also set them from outside diff --git a/webinstall/build.yaml b/webinstall/build.yaml index 300ffcd..9177f89 100644 --- a/webinstall/build.yaml +++ b/webinstall/build.yaml @@ -36,33 +36,6 @@ # # 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 @@ -131,7 +104,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 +139,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 +174,15 @@ types: - 38 - 39 + - &protogpio + - {label: unset,value:} + - PPIN19 + - PPIN21 + - PPIN22 + - PPIN23 + - PPIN25 + - PPIN33 + - &serialRX <<: *gpioinput key: RX @@ -442,7 +428,56 @@ types: values: - key: true value: GWBME280#busname#2 + - &m5protochildren + - *serial1 + - *serial2 + - *can + - <<: *iicsensors + base: + busname: "1" + bus: "" + - <<: *iicsensors + base: + busname: "2" + bus: "2" + + + - &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 resources: default: &esp32default @@ -457,6 +492,9 @@ config: target: environment label: 'Board' key: board + base: + gpiopinv: *gpiopinv + gpioinputv: *gpioinputv values: - value: m5stack-atom-generic label: m5stack-atom diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index e1ebc2f..60220c9 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -573,7 +573,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,8 +583,17 @@ class PipelineInfo{ if (! base) return str; if (typeof(str) === 'string'){ for (let k in base){ - let r=new RegExp("#"+k+"#","g"); - str=str.replace(r,base[k]); + 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; } @@ -598,7 +607,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; } From a87546bfdf1248d5385d0f4d4104350624c6d477 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 3 Mar 2024 20:52:10 +0100 Subject: [PATCH 12/35] add DMS22B to build service --- lib/spitask/GWDMS22B.cpp | 13 +++--- lib/spitask/GwSpiSensor.h | 4 +- lib/spitask/GwSpiTask.cpp | 49 ++++++++++----------- webinstall/build.yaml | 89 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 122 insertions(+), 33 deletions(-) diff --git a/lib/spitask/GWDMS22B.cpp b/lib/spitask/GWDMS22B.cpp index fcd51f1..7c3942f 100644 --- a/lib/spitask/GWDMS22B.cpp +++ b/lib/spitask/GWDMS22B.cpp @@ -15,6 +15,7 @@ #include "GWDMS22B.h" #include "GwApi.h" #include "N2kMessages.h" +#include "GwHardware.h" #define CHECK_BUS(BUS) \ @@ -28,7 +29,7 @@ } #ifdef GWDMS22B11 - #define ADD22B11 ADD22B(DMS22B11,SPI0) + #define ADD22B11 ADD22B(DMS22B11,SPI1) #ifndef GWDMS22B11_CS #define GWDMS22B11_CS -1 #endif @@ -38,7 +39,7 @@ #endif #ifdef GWDMS22B12 - #define ADD22B12 ADD22B(DMS22B12,SPI0) + #define ADD22B12 ADD22B(DMS22B12,SPI1) #ifndef GWDMS22B12_CS #error "you need to define GWDMS22B12_CS" #endif @@ -51,7 +52,7 @@ #endif #ifdef GWDMS22B13 - #define ADD22B13 ADD22B(DMS22B13,SPI0) + #define ADD22B13 ADD22B(DMS22B13,SPI1) #ifndef GWDMS22B13_CS #error "you need to define GWDMS22B13_CS" #endif @@ -64,7 +65,7 @@ #endif #ifdef GWDMS22B21 - #define ADD22B21 ADD22B(DMS22B21,SPI1) + #define ADD22B21 ADD22B(DMS22B21,SPI2) #ifndef GWDMS22B21_CS #define GWDMS22B21_CS -1 #endif @@ -74,7 +75,7 @@ #endif #ifdef GWDMS22B22 - #define ADD22B22 ADD22B(DMS22B22,SPI1) + #define ADD22B22 ADD22B(DMS22B22,SPI2) #ifndef GWDMS22B22_CS #error "you need to define GWDMS22B22_CS" #endif @@ -87,7 +88,7 @@ #endif #ifdef GWDMS22B23 - #define ADD22B23 ADD22B(DMS22B23,SPI1) + #define ADD22B23 ADD22B(DMS22B23,SPI2) #ifndef GWDMS22B23_CS #error "you need to define GWDMS22B23_CS" #endif diff --git a/lib/spitask/GwSpiSensor.h b/lib/spitask/GwSpiSensor.h index 48e4a78..80beabd 100644 --- a/lib/spitask/GwSpiSensor.h +++ b/lib/spitask/GwSpiSensor.h @@ -136,6 +136,6 @@ class SSISensor : public SensorBase{ }; using SpiSensorList=SensorList; -#define GWSPI0_HOST SPI2_HOST -#define GWSPI1_HOST SPI3_HOST +#define GWSPI1_HOST SPI2_HOST +#define GWSPI2_HOST SPI3_HOST #endif \ No newline at end of file diff --git a/lib/spitask/GwSpiTask.cpp b/lib/spitask/GwSpiTask.cpp index daa34b0..e9fff28 100644 --- a/lib/spitask/GwSpiTask.cpp +++ b/lib/spitask/GwSpiTask.cpp @@ -16,28 +16,13 @@ #include "GwSpiSensor.h" #include "GWDMS22B.h" #include "GwTimer.h" +#include "GwHardware.h" -static SPIBus bus1(GWSPI0_HOST); -static SPIBus bus2(GWSPI1_HOST); +static SPIBus bus1(GWSPI1_HOST); +static SPIBus bus2(GWSPI2_HOST); static SpiSensorList sensors; -#ifdef GWSPI0_CLK -static const int spi0clk=GWSPI0_CLK; -#else -static const int spi0clk=-1; -#endif -#ifdef GWSPI0_MISO -static const int spi0miso=GWSPI0_MISO; -#else -static const int spi0miso=-1; -#endif -#ifdef GWSPI0_MOSI -static const int spi0mosi=GWSPI0_MOSI; -#else -static const int spi0mosi=-1; -#endif - #ifdef GWSPI1_CLK static const int spi1clk=GWSPI1_CLK; #else @@ -54,6 +39,22 @@ static const int spi1mosi=GWSPI1_MOSI; 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 buses; @@ -63,22 +64,22 @@ void runSpiTask(GwApi *api){ if (bus == buses.end()){ switch (busId) { - case GWSPI0_HOST: - if (spi0clk < 0){ + 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,spi0mosi,spi0miso,spi0clk)){ + if (bus1.init(logger,spi1mosi,spi1miso,spi1clk)){ buses[busId]=&bus1; } } break; - case GWSPI1_HOST: - if (spi1clk < 0){ + 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,spi1mosi,spi1miso,spi1clk)){ + if (bus2.init(logger,spi2mosi,spi2miso,spi2clk)){ buses[busId]=&bus2; } } diff --git a/webinstall/build.yaml b/webinstall/build.yaml index 9177f89..efd05f0 100644 --- a/webinstall/build.yaml +++ b/webinstall/build.yaml @@ -428,6 +428,78 @@ types: values: - key: true value: GWBME280#busname#2 + + - &spisensors + type: checkbox + label: "SPI/SSI #busname#" + key: "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" + - type: checkbox + label: GWDMS22B-#busname#-3 + description: "DMS22B rotatory encoder (SSI)" + key: dms22b#busname#3 + target: define + url: "https://www.mouser.de/datasheet/2/54/bour_s_a0011704065_1-2262614.pdf" + values: + - key: true + value: GWDMS22B#busname#3 + children: + - <<: *gpiopin + label: CS + key: dms22b#busname#3cs + description: "chip select pin, only necessary for multiple devices on this bus" + target: "define:GWDMS22B#busname#3_CS" + + - &m5protochildren - *serial1 - *serial2 @@ -440,7 +512,14 @@ types: base: busname: "2" bus: "2" - + - <<: *spisensors + base: + busname: "1" + bus: "1" + - <<: *spisensors + base: + busname: "2" + bus: "2" - &m5base @@ -538,5 +617,13 @@ config: bus: "" - <<: *iicsensors base: + busname: "2" + bus: "2" + - <<: *spisensors + base: + busname: "1" + bus: "1" + - <<: *spisensors + base: busname: "2" bus: "2" \ No newline at end of file From 32e48b126c483da0f5c0269269bd60c36b166062 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 17 Mar 2024 15:58:10 +0100 Subject: [PATCH 13/35] limit DMS22B to 4 --- lib/spitask/GWDMS22B.cpp | 28 ---------------------------- lib/spitask/config.json | 12 ------------ lib/spitask/platformio.ini | 4 ++-- webinstall/build.yaml | 16 ---------------- 4 files changed, 2 insertions(+), 58 deletions(-) diff --git a/lib/spitask/GWDMS22B.cpp b/lib/spitask/GWDMS22B.cpp index 7c3942f..0cf2218 100644 --- a/lib/spitask/GWDMS22B.cpp +++ b/lib/spitask/GWDMS22B.cpp @@ -51,18 +51,6 @@ #define ADD22B12 #endif -#ifdef GWDMS22B13 - #define ADD22B13 ADD22B(DMS22B13,SPI1) - #ifndef GWDMS22B13_CS - #error "you need to define GWDMS22B13_CS" - #endif - #if GWDMS22B11_CS == -1 - #error "multiple devices on one SPI bus need chip select defines - GWDMS22B11_CS is unset" - #endif -#else - #define GWDMS22B13_CS -1 - #define ADD22B13 -#endif #ifdef GWDMS22B21 #define ADD22B21 ADD22B(DMS22B21,SPI2) @@ -87,18 +75,6 @@ #define ADD22B22 #endif -#ifdef GWDMS22B23 - #define ADD22B23 ADD22B(DMS22B23,SPI2) - #ifndef GWDMS22B23_CS - #error "you need to define GWDMS22B23_CS" - #endif - #if GWDMS22B21_CS == -1 - #error "multiple devices on one SPI bus need chip select defines - GWDMS22B11_CS is unset" - #endif -#else - #define GWDMS22B23_CS -1 - #define ADD22B23 -#endif @@ -144,10 +120,8 @@ class GWDMS22B : public SSISensor{ virtual void readConfig(GwConfigHandler *cfg){ DMS22B(DMS22B11); DMS22B(DMS22B12); - DMS22B(DMS22B13); DMS22B(DMS22B21); DMS22B(DMS22B22); - DMS22B(DMS22B23); intv=1000*fintv; } }; @@ -155,9 +129,7 @@ class GWDMS22B : public SSISensor{ void registerDMS22B(GwApi *api,SpiSensorList &sensors){ ADD22B11 ADD22B12 - ADD22B13 ADD22B21 ADD22B22 - ADD22B23 } diff --git a/lib/spitask/config.json b/lib/spitask/config.json index 8b1ece2..cb96db6 100644 --- a/lib/spitask/config.json +++ b/lib/spitask/config.json @@ -13,11 +13,6 @@ "i": "12", "n": "1" }, - { - "b": "1", - "i": "13", - "n": "13" - }, { "b": "2", "i": "21", @@ -27,14 +22,7 @@ "b": "2", "i": "22", "n": "22" - }, - { - "b": "2", - "i": "23", - "n": "23" } - - ], "children": [ { diff --git a/lib/spitask/platformio.ini b/lib/spitask/platformio.ini index a6d5737..d0c8f21 100644 --- a/lib/spitask/platformio.ini +++ b/lib/spitask/platformio.ini @@ -7,8 +7,8 @@ lib_deps = ${env.lib_deps} ${sensors.lib_deps} build_flags= - -D GWSPI0_CLK=21 - -D GWSPI0_MISO=25 + -D GWSPI1_CLK=21 + -D GWSPI1_MISO=25 -D GWDMS22B11 -D GWDMS22B11_CS=22 ${env.build_flags} diff --git a/webinstall/build.yaml b/webinstall/build.yaml index efd05f0..e750233 100644 --- a/webinstall/build.yaml +++ b/webinstall/build.yaml @@ -483,22 +483,6 @@ types: key: dms22b#busname#2cs description: "chip select pin, only necessary for multiple devices on this bus" target: "define:GWDMS22B#busname#2_CS" - - type: checkbox - label: GWDMS22B-#busname#-3 - description: "DMS22B rotatory encoder (SSI)" - key: dms22b#busname#3 - target: define - url: "https://www.mouser.de/datasheet/2/54/bour_s_a0011704065_1-2262614.pdf" - values: - - key: true - value: GWDMS22B#busname#3 - children: - - <<: *gpiopin - label: CS - key: dms22b#busname#3cs - description: "chip select pin, only necessary for multiple devices on this bus" - target: "define:GWDMS22B#busname#3_CS" - - &m5protochildren - *serial1 From a240cade7e8e9a50f71eaecd56e4c08343f86eef Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 17 Mar 2024 18:20:09 +0100 Subject: [PATCH 14/35] add calset input --- lib/api/GwApi.h | 7 ++++ lib/gwwebserver/GwWebServer.cpp | 4 ++ lib/gwwebserver/GwWebServer.h | 2 + lib/spitask/GWDMS22B.cpp | 4 ++ lib/spitask/config.json | 2 +- lib/usercode/GwUserCode.cpp | 3 ++ src/main.cpp | 44 ++++++++++++++++++++- web/index.html | 5 ++- web/index.js | 69 ++++++++++++++++++++++++++++++--- 9 files changed, 131 insertions(+), 9 deletions(-) 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/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/spitask/GWDMS22B.cpp b/lib/spitask/GWDMS22B.cpp index 0cf2218..91c4964 100644 --- a/lib/spitask/GWDMS22B.cpp +++ b/lib/spitask/GWDMS22B.cpp @@ -81,6 +81,7 @@ class GWDMS22B : public SSISensor{ int zero=2047; bool invt=false; + String zeroConfigName; public: using SSISensor::SSISensor; virtual bool preinit(GwApi * api){ @@ -102,6 +103,8 @@ class GWDMS22B : public SSISensor{ 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) {\ @@ -109,6 +112,7 @@ class GWDMS22B : public SSISensor{ CFG_GET(iid,PRFX); \ CFG_GET(fintv,PRFX); \ CFG_GET(zero,PRFX); \ + zeroConfigName=GwConfigDefinitions::PRFX ## zero;\ CFG_GET(invt,PRFX); \ bits=12; \ clock=500000; \ diff --git a/lib/spitask/config.json b/lib/spitask/config.json index cb96db6..1b96171 100644 --- a/lib/spitask/config.json +++ b/lib/spitask/config.json @@ -67,7 +67,7 @@ { "name": "DMS22B$izero", "label": "DMS22B$i Zero", - "type": "number", + "type": "calset", "default": 2048, "description": "Zero position (0...2^bits-1)", "category": "spisensors$b", diff --git a/lib/usercode/GwUserCode.cpp b/lib/usercode/GwUserCode.cpp index ef6fbd7..4522c92 100644 --- a/lib/usercode/GwUserCode.cpp +++ b/lib/usercode/GwUserCode.cpp @@ -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); + } }; diff --git a/src/main.cpp b/src/main.cpp index 5ce1b7a..6a6005b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -235,15 +235,38 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg, int sourceId,bool conv }); } +class CalibrationValues { + using Map=std::map; + 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 calibrations; public: ApiImpl(int sourceId) { this->sourceId = sourceId; + calibrations.reset(new CalibrationValues()); } virtual GwRequestQueue *getQueue() { @@ -332,6 +355,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(){ @@ -803,7 +833,19 @@ void setup() { [](AsyncWebServerRequest *request){ }, - handleConfigRequestData); + 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(); diff --git a/web/index.html b/web/index.html index da16fc8..7172b41 100644 --- a/web/index.html +++ b/web/index.html @@ -131,7 +131,6 @@ AHA
-
@@ -190,6 +189,10 @@ + diff --git a/web/index.js b/web/index.js index bc5bcdf..92a58ea 100644 --- a/web/index.js +++ b/web/index.js @@ -371,22 +371,37 @@ function updateMsgDetails(key, details) { }); } -function showOverlay(text, isHtml) { +function showOverlay(text, isHtml,buttons) { let el = document.getElementById('overlayContent'); - if (isHtml) { - el.innerHTML = text; - el.classList.remove("text"); + if (text instanceof Object){ + el.textContent=''; + el.appendChild(text); } else { - el.textContent = text; - el.classList.add("text"); + if (isHtml) { + el.innerHTML = text; + el.classList.remove("text"); + } + else { + 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 +471,45 @@ 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.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 createInput(configItem, frame,clazz) { let el; if (configItem.type === 'boolean' || configItem.type === 'list' || configItem.type == 'boatData') { @@ -492,6 +546,9 @@ function createInput(configItem, frame,clazz) { if (configItem.type === 'xdr'){ return createXdrInput(configItem,frame,clazz); } + if (configItem.type === "calset"){ + return createCalSetInput(configItem,frame,clazz); + } el = addEl('input',clazz,frame); if (configItem.readOnly) el.setAttribute('disabled',true); el.setAttribute('name', configItem.name) From 101d551f93c1d41b2a6b8fb8aa91ecf1bcf2947d Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 17 Mar 2024 19:01:53 +0100 Subject: [PATCH 15/35] add calset and calval input types --- lib/spitask/config.json | 2 +- web/index.css | 3 +++ web/index.html | 7 ++++++ web/index.js | 54 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/lib/spitask/config.json b/lib/spitask/config.json index 1b96171..242c16c 100644 --- a/lib/spitask/config.json +++ b/lib/spitask/config.json @@ -69,7 +69,7 @@ "label": "DMS22B$i Zero", "type": "calset", "default": 2048, - "description": "Zero position (0...2^bits-1)", + "description": "Zero position (0...2^bits-1)\nuse the \"C\" button to open a calibrate dialog", "category": "spisensors$b", "capabilities": { "DMS22B$i": "true" diff --git a/web/index.css b/web/index.css index c4db397..c3ea7ee 100644 --- a/web/index.css +++ b/web/index.css @@ -114,6 +114,9 @@ body { display: flex; align-items: center; } + .value button { + margin-left: 0.5em; + } .hidden{ display: none !important; } diff --git a/web/index.html b/web/index.html index 7172b41..cc17706 100644 --- a/web/index.html +++ b/web/index.html @@ -193,6 +193,13 @@

+ diff --git a/web/index.js b/web/index.js index 92a58ea..4c90e44 100644 --- a/web/index.js +++ b/web/index.js @@ -479,7 +479,7 @@ function createCalSetInput(configItem,frame,clazz){ cb.addEventListener('click',(ev)=>{ let cs=document.getElementById("calset").cloneNode(true); cs.classList.remove("hidden"); - cs.querySelector(".heading").textContent=configItem.name; + cs.querySelector(".heading").textContent=configItem.label||configItem.name; let vel=cs.querySelector(".val"); if (caliv != 0) window.clearInterval(caliv); caliv=window.setInterval(()=>{ @@ -510,6 +510,55 @@ function createCalSetInput(configItem,frame,clazz){ 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') { @@ -549,6 +598,9 @@ function createInput(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) From 8641f0efd93ffb8cfc9c88c08ac800ac2eba4a5a Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 18 Mar 2024 11:02:03 +0100 Subject: [PATCH 16/35] CFG_INIT for initial cfg values --- extra_script.py | 25 ++----------------------- lib/config/GWConfig.cpp | 23 ++++++++++++++++++++++- lib/config/GWConfig.h | 1 + lib/hardware/GwHardware.h | 39 +++++++++++++++++++++++++++++---------- 4 files changed, 54 insertions(+), 34 deletions(-) diff --git a/extra_script.py b/extra_script.py index ea5a8f7..51c484a 100644 --- a/extra_script.py +++ b/extra_script.py @@ -185,34 +185,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): diff --git a/lib/config/GWConfig.cpp b/lib/config/GWConfig.cpp index 28528c4..fbe1862 100644 --- a/lib/config/GWConfig.cpp +++ b/lib/config/GWConfig.cpp @@ -4,6 +4,19 @@ #include #include #include +using CfgInit=std::function; +static std::vector cfgInits; +class CfgInitializer{ + public: + CfgInitializer(CfgInit f){ + cfgInits.push_back(f); + } +}; +#define CFG_INIT(name,value,mode) \ + __MSG("config set " #name " " #value " " #mode); \ + static CfgInitializer _ ## name ## _init([](GwConfigHandler *cfg){ \ + cfg->setValue(GwConfigDefinitions::name,value,GwConfigInterface::mode); \ + }); #include "GwHardware.h" #include "GwConfigDefImpl.h" @@ -61,6 +74,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 +142,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 6264506..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(); diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 6680e3c..2227cd2 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -24,6 +24,9 @@ #include #include "GwAppInfo.h" #include "GwUserTasks.h" +#ifndef CFG_INIT + #define CFG_INIT(name,value,mode) +#endif //general definitions for M5AtomLite //hint for groove pins: @@ -162,7 +165,7 @@ #define GWSERIAL_RX BOARD_LEFT1 #define GWSERIAL_TYPE GWSERIAL_TYPE_UNI #endif - +CFG_INIT(serialBaud,"9600",READONLY) //M5 GPS (Atomic GPS Base) #ifdef M5_GPS_KIT #ifdef _GWM5_BOARD @@ -171,12 +174,15 @@ #define _GWM5_BOARD #define GWSERIAL_RX BOARD_LEFT1 #define GWSERIAL_TYPE GWSERIAL_TYPE_RX - #define CFGDEFAULT_serialBaud "9600" - #define CFGMODE_serialBaud GwConfigInterface::READONLY + CFG_INIT(serialBaud,"9600",READONLY) #endif //M5 ProtoHub #ifdef M5_PROTO_HUB + #ifdef _GWM5_BOARD + #error "can only define one M5 base" + #endif + #define _GWM5_BOARD #define PPIN22 BOARD_LEFT1 #define PPIN19 BOARD_LEFT2 #define PPIN23 BOARD_LEFT3 @@ -185,6 +191,20 @@ #define PPIN25 BOARD_RIGHT2 #endif +//M5 PortABC extension +#ifdef M5_PORTABC + #ifdef _GWM5_BOARD + #error "can only define one M5 base" + #endif + #define _GWM5_BOARD + #define ABC_PAYELLOW BOARD_RIGHT2 + #define ABC_PAWHITE BOARD_RIGHT1 + #define ABC_PBYELLOW BOARD_LEFT3 + #define ABC_PBWHITE BOARD_LEFT4 + #define ABC_PCYELLOW BOARD_LEFT1 + #define ABC_PCWHITE BOARD_LEFT2 +#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 @@ -228,13 +248,11 @@ #ifdef GWSERIAL_TYPE #define GWSERIAL2_RX GROOVE_PIN_1 #define GWSERIAL2_TYPE GWSERIAL_TYPE_RX - #define CFGDEFAULT_serialBaud "9600" - #define CFGMODE_serialBaud GwConfigInterface::READONLY + CFG_INIT(serialBaud,"9600",READONLY) #else #define GWSERIAL_RX GROOVE_PIN_1 #define GWSERIAL_TYPE GWSERIAL_TYPE_RX - #define CFGDEFAULT_serial2Baud "9600" - #define CFGMODE_serial2Baud GwConfigInterface::READONLY + CFG_INIT(serial2Baud,"9600",READONLY) #endif #endif @@ -332,12 +350,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 From 59a80fcc9a0de7a2850b30b37928ffa306490cf4 Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 18 Mar 2024 20:20:38 +0100 Subject: [PATCH 17/35] intermediate: prepare for multiple grove configs --- lib/channel/GwChannelList.cpp | 39 +++++++++++++++- lib/channel/GwChannelList.h | 3 ++ lib/config/GWConfig.cpp | 8 +--- lib/config/GwConfigItem.h | 11 ++++- lib/hardware/GwHardware.h | 85 ++++++++++++----------------------- webinstall/build.yaml | 24 +++++++++- webinstall/cibuild.js | 7 ++- 7 files changed, 107 insertions(+), 70 deletions(-) diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp index c094a0a..0f2f3ec 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -1,5 +1,19 @@ #include "GwChannelList.h" #include "GwApi.h" + +using SerInitFunction=std::function; +std::vector initFunctions; + +#define CFG_EXP(ser) GwChannelList::ser +#define CFG_SERIAL(ser,rx,tx,mode) \ + __MSG("serial config " __STR(ser) " rx=" __STR(rx) ", tx=" __STR(tx) ",mode=" __STR(mode)); \ + static GwInitializer _ ## name ## _init( \ + initFunctions,[](GwChannelList *cl){cl->addSerial(CFG_EXP(ser),rx,tx,mode);}); +//check for duplicate groove usages +#define __GR_EXP(GROOVE) __groveuse_ ## GROOVE +#define GROVE_USE(USER) \ + __MSG("grove " __STR(USER) " used by " #USER) \ + static int __GR_EXP(USER) =1; #include "GwHardware.h" #include "GwSocketServer.h" #include "GwSerial.h" @@ -101,8 +115,18 @@ static SerialParam *getSerialParam(int id){ } return nullptr; } - -void GwChannelList:: addSerial(HardwareSerial *stream,int id,int type,int rx,int tx){ +void GwChannelList::addSerial(const String &name, int rx, int tx, int type){ + if (name == serial){ + addSerial(&Serial1,SERIAL1_CHANNEL_ID,type,rx,tx); + return; + } + if (name == serial2){ + addSerial(&Serial2,SERIAL2_CHANNEL_ID,type,rx,tx); + return; + } + LOG_DEBUG(GwLog::ERROR,"invalid serial config") +} +void GwChannelList::addSerial(HardwareSerial *stream,int id,int type,int rx,int tx){ const char *mode=nullptr; switch (type) { @@ -126,6 +150,12 @@ void GwChannelList:: addSerial(HardwareSerial *stream,int id,int type,int rx,int addSerial(stream,id,mode,rx,tx); } void GwChannelList::addSerial(HardwareSerial *serialStream,int id,const String &mode,int rx,int tx){ + 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); @@ -223,6 +253,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:initFunctions){ + init(this); + } + //handle separate defines //serial 1 #ifndef GWSERIAL_TX #define GWSERIAL_TX -1 diff --git a/lib/channel/GwChannelList.h b/lib/channel/GwChannelList.h index 1bddfca..3aca4cc 100644 --- a/lib/channel/GwChannelList.h +++ b/lib/channel/GwChannelList.h @@ -33,6 +33,9 @@ class GwChannelList{ 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); public: + static constexpr const char* serial="serial"; + static constexpr const char* serial2="serial2"; + void addSerial(const String &name, int rx, int tx, int type); GwChannelList(GwLog *logger, GwConfigHandler *config); typedef std::function ChannelAction; void allChannels(ChannelAction action); diff --git a/lib/config/GWConfig.cpp b/lib/config/GWConfig.cpp index fbe1862..e53870a 100644 --- a/lib/config/GWConfig.cpp +++ b/lib/config/GWConfig.cpp @@ -6,15 +6,9 @@ #include using CfgInit=std::function; static std::vector cfgInits; -class CfgInitializer{ - public: - CfgInitializer(CfgInit f){ - cfgInits.push_back(f); - } -}; #define CFG_INIT(name,value,mode) \ __MSG("config set " #name " " #value " " #mode); \ - static CfgInitializer _ ## name ## _init([](GwConfigHandler *cfg){ \ + static GwInitializer _ ## name ## _init(cfgInits,[](GwConfigHandler *cfg){ \ cfg->setValue(GwConfigDefinitions::name,value,GwConfigInterface::mode); \ }); #include "GwHardware.h" diff --git a/lib/config/GwConfigItem.h b/lib/config/GwConfigItem.h index d739902..058dc91 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: @@ -79,4 +78,14 @@ class GwNmeaFilter{ #define __XSTR(x) __STR(x) #define __STR(x) #x #define __MSG(x) _Pragma (__STR(message (x))) + +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/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 2227cd2..7387c8a 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -25,9 +25,16 @@ #include "GwAppInfo.h" #include "GwUserTasks.h" #ifndef CFG_INIT - #define CFG_INIT(name,value,mode) + #define CFG_INIT(...) #endif - +#define CFG_INITP(prefix,suffix,value,mode) CFG_INIT(prefix ## suffix,value,mode) +#ifndef CFG_SERIAL + #define CFG_SERIAL(...) +#endif +#ifndef GROVE_USE + #define GROVE_USE(...) +#endif +#define _GW_GROOVE_SERIAL serial //general definitions for M5AtomLite //hint for groove pins: //according to some schematics the numbering is 1,2,3(VCC),4(GND) @@ -71,7 +78,6 @@ #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 @@ -150,9 +156,9 @@ //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 + CFG_SERIAL(serial,BOARD_LEFT1,BOARD_LEFT2,GWSERIAL_TYPE_BI) + #undef _GW_GROOVE_SERIAL + #define _GW_GROOVE_SERIAL serial2 #endif //M5 Serial (Atomic RS485 Base) @@ -161,19 +167,19 @@ #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 + CFG_SERIAL(serial,BOARD_LEFT1,BOARD_LEFT2,GWSERIAL_TYPE_UNI) + #undef _GW_GROOVE_SERIAL + #define _GW_GROOVE_SERIAL serial2 #endif -CFG_INIT(serialBaud,"9600",READONLY) //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 + CFG_SERIAL(serial,BOARD_LEFT1,-1,GWSERIAL_TYPE_UNI) + #undef _GW_GROOVE_SERIAL + #define _GW_GROOVE_SERIAL serial2 CFG_INIT(serialBaud,"9600",READONLY) #endif @@ -212,48 +218,19 @@ CFG_INIT(serialBaud,"9600",READONLY) //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 + GROVE_USE(SERIAL_GROOVE_485) + CFG_SERIAL(_GW_GROOVE_SERIAL,GROOVE_PIN_1,GROOVE_PIN_2,GWSERIAL_TYPE_UNI) #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 + GROVE_USE(SERIAL_GROOVE_232) + CFG_SERIAL(_GW_GROOVE_SERIAL,GROOVE_PIN_1,GROOVE_PIN_2,GWSERIAL_TYPE_BI) #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 - CFG_INIT(serialBaud,"9600",READONLY) - #else - #define GWSERIAL_RX GROOVE_PIN_1 - #define GWSERIAL_TYPE GWSERIAL_TYPE_RX - CFG_INIT(serial2Baud,"9600",READONLY) - #endif + GROVE_USE(M5_GPS_UNIT) + CFG_SERIAL(_GW_GROOVE_SERIAL,GROOVE_PIN_1,-1,GWSERIAL_TYPE_RX) + CFG_INITP(_GW_GROOVE_SERIAL,Baud,"9600",READONLY) #endif //can kit for M5 Atom @@ -267,17 +244,14 @@ CFG_INIT(serialBaud,"9600",READONLY) #endif //CAN via groove #ifdef M5_CANUNIT - #ifdef _GWM5_GROOVE - #error "can only have one M5 groove" - #endif - #define _GWM5_GROOVE + GROVE_USE(M5_CANUNIT) #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 + #define M5_GROOVEIIC M5_ENV3 #endif #ifndef GWSHT3X #define GWSHT3X -1 @@ -288,10 +262,7 @@ CFG_INIT(serialBaud,"9600",READONLY) #endif #ifdef M5_GROOVEIIC - #ifdef _GWM5_GROOVE - #error "can only have one M5 groove" - #endif - #define _GWM5_GROOVE + GROVE_USE(M5_GROOVEIIC) #ifdef GWIIC_SCL #error "you cannot define both GWIIC_SCL and M5_GROOVEIIC" #endif diff --git a/webinstall/build.yaml b/webinstall/build.yaml index e750233..d583efc 100644 --- a/webinstall/build.yaml +++ b/webinstall/build.yaml @@ -101,7 +101,7 @@ types: children: - *m5grooveserial - &gpiopin - type: dropdown + type: "#gpiotype#" resource: "gpio:" help: 'Select the number of the GPIO pin for this function' values: "#gpiopinv#" @@ -136,7 +136,7 @@ types: - 38 - &gpioinput - type: dropdown + type: "#gpiotype#" resource: "gpio:" help: 'Select the number of the GPIO pin for this function' values: "#gpiopinv#" @@ -183,6 +183,16 @@ types: - PPIN25 - PPIN33 + - &abcgpio + - {label:unset, value:} + - ABC_PAYELLOW + - ABC_PAYWHITE + - ABC_PBYELLOW + - ABC_PBYWHITE + - ABC_PBYELLOW + - ABC_PBYWHITE + + - &serialRX <<: *gpioinput key: RX @@ -235,6 +245,7 @@ types: type: checkbox label: 'Serial 1' key: serial1 + resource: serial1 base: serial: GWSERIAL_ values: *serialValues @@ -243,6 +254,7 @@ types: type: checkbox label: 'Serial 2' key: serial2 + resource: serial2 base: serial: GWSERIAL2_ values: *serialValues @@ -360,6 +372,7 @@ types: type: checkbox label: "I2C #busname#" key: "i2c#busname#" + resource: "i2c#busname#" description: "I2C Bus #busname#" values: - key: true @@ -433,6 +446,7 @@ types: type: checkbox label: "SPI/SSI #busname#" key: "spi#busname#" + resource: "spi#busname#" description: "SPI(SSI) Bus #busname#" values: - key: true @@ -541,6 +555,11 @@ types: 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" + base: resources: default: &esp32default @@ -558,6 +577,7 @@ config: base: gpiopinv: *gpiopinv gpioinputv: *gpioinputv + gpiotype: dropdown values: - value: m5stack-atom-generic label: m5stack-atom diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index 60220c9..dc87fa6 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -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); From 746c9e3da001c5ca6f1d29120a15da1492425070 Mon Sep 17 00:00:00 2001 From: andreas Date: Tue, 19 Mar 2024 20:54:32 +0100 Subject: [PATCH 18/35] intermediate: multiple groves --- lib/channel/GwChannelList.cpp | 22 ++--- lib/channel/GwChannelList.h | 4 +- lib/config/GwConfigItem.h | 7 +- lib/hardware/GwHardware.h | 155 ++++++++++++++++++++-------------- src/main.cpp | 5 +- 5 files changed, 107 insertions(+), 86 deletions(-) diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp index 0f2f3ec..0fa8b52 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -4,16 +4,12 @@ using SerInitFunction=std::function; std::vector initFunctions; -#define CFG_EXP(ser) GwChannelList::ser -#define CFG_SERIAL(ser,rx,tx,mode) \ - __MSG("serial config " __STR(ser) " rx=" __STR(rx) ", tx=" __STR(tx) ",mode=" __STR(mode)); \ - static GwInitializer _ ## name ## _init( \ - initFunctions,[](GwChannelList *cl){cl->addSerial(CFG_EXP(ser),rx,tx,mode);}); +#define CFG_SERIAL(id,ser,rx,tx,mode) \ + __MSG("serial config " #id __XSTR(ser) "(" #ser ")" " rx=" __XSTR(rx) ", tx=" __XSTR(tx) ",mode=" __STR(mode)); \ + static GwInitializer __serial ## id ## _init \ + (initFunctions,[](GwChannelList *cl){cl->addSerial(ser,rx,tx,mode);}); //check for duplicate groove usages -#define __GR_EXP(GROOVE) __groveuse_ ## GROOVE -#define GROVE_USE(USER) \ - __MSG("grove " __STR(USER) " used by " #USER) \ - static int __GR_EXP(USER) =1; +#define GW_PINDEFS #include "GwHardware.h" #include "GwSocketServer.h" #include "GwSerial.h" @@ -115,16 +111,16 @@ static SerialParam *getSerialParam(int id){ } return nullptr; } -void GwChannelList::addSerial(const String &name, int rx, int tx, int type){ - if (name == serial){ +void GwChannelList::addSerial(int id, int rx, int tx, int type){ + if (id == 1){ addSerial(&Serial1,SERIAL1_CHANNEL_ID,type,rx,tx); return; } - if (name == serial2){ + if (id == 2){ addSerial(&Serial2,SERIAL2_CHANNEL_ID,type,rx,tx); return; } - LOG_DEBUG(GwLog::ERROR,"invalid serial config") + LOG_DEBUG(GwLog::ERROR,"invalid serial config with id %d",id); } void GwChannelList::addSerial(HardwareSerial *stream,int id,int type,int rx,int tx){ const char *mode=nullptr; diff --git a/lib/channel/GwChannelList.h b/lib/channel/GwChannelList.h index 3aca4cc..c8d3bda 100644 --- a/lib/channel/GwChannelList.h +++ b/lib/channel/GwChannelList.h @@ -33,9 +33,7 @@ class GwChannelList{ 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); public: - static constexpr const char* serial="serial"; - static constexpr const char* serial2="serial2"; - void addSerial(const String &name, int rx, int tx, int type); + void addSerial(int id, int rx, int tx, int type); GwChannelList(GwLog *logger, GwConfigHandler *config); typedef std::function ChannelAction; void allChannels(ChannelAction action); diff --git a/lib/config/GwConfigItem.h b/lib/config/GwConfigItem.h index 058dc91..9370158 100644 --- a/lib/config/GwConfigItem.h +++ b/lib/config/GwConfigItem.h @@ -77,8 +77,13 @@ 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: diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 7387c8a..d0f00b8 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -31,16 +31,36 @@ #ifndef CFG_SERIAL #define CFG_SERIAL(...) #endif -#ifndef GROVE_USE - #define GROVE_USE(...) -#endif -#define _GW_GROOVE_SERIAL serial + +#ifdef GW_PINDEFS + #define GWRESOURCE_USE(RES,USER) \ + __MSG(__STR(RES) __STR(USER) " used by " #USER) \ + static int __EXPAND3(_resourceUsed,RES,USER) =1; + #define GWBASE_USE(USER) \ + __MSG("base unit " #USER) \ + static int _resourceUsedBase=1; + #define GW_SETGROOVE(prfx,p1,p2) \ + static int __EXPAND3(GROOVEPIN_,prfx,1) = p1; \ + static int __EXPAND3(GROOVEPIN_,prfx,2) = p2; + #define GW_GETGROOVE(prfx,pin) \ + __EXPAND3(GROOVEPIN_,prfx,pin) + #define GW_SETRES(res,groove,val) \ + static int __EXPAND3(_resource,res,groove) = val; + #define GW_GETRES(res,groove) \ + __EXPAND3(_resource,res,groove) +#else + #define GW_SETGROOVE(...) + #define GW_GETGROOVE(...) + #define GW_SETRES(...) + #define GW_GETRES(...) + #define GWRESOURCE_USE(...) + #define GWBASE_USE(...) +#endif //general definitions for M5AtomLite //hint for groove pins: //according to some schematics the numbering is 1,2,3(VCC),4(GND) #ifdef PLATFORM_BOARD_M5STACK_ATOM - #define GROOVE_PIN_2 GPIO_NUM_26 - #define GROOVE_PIN_1 GPIO_NUM_32 + GW_SETGROOVE(1,GPIO_NUM_32,GPIO_NUM_26) #define GWBUTTON_PIN GPIO_NUM_39 #define GWLED_FASTLED #define GWLED_TYPE SK6812 @@ -58,10 +78,10 @@ #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 - #define GROOVE_PIN_1 GPIO_NUM_1 + GW_SETGROOVE(1,GPIO_NUM_1,GPIO_NUM_2) #define GWBUTTON_PIN GPIO_NUM_41 #define GWLED_FASTLED #define GWLED_TYPE WS2812 @@ -80,8 +100,7 @@ #endif //M5Stick C #ifdef PLATFORM_BOARD_M5STICK_C - #define GROOVE_PIN_2 GPIO_NUM_32 - #define GROOVE_PIN_1 GPIO_NUM_31 + GW_SETGROOVE(1,GPIO_NUM_31,GPIO_NUM_32) #define USBSerial Serial #endif @@ -95,13 +114,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 @@ -111,33 +130,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 @@ -155,40 +174,28 @@ //M5 Serial (Atomic RS232 Base) #ifdef M5_SERIAL_KIT_232 - #define _GWM5_BOARD - CFG_SERIAL(serial,BOARD_LEFT1,BOARD_LEFT2,GWSERIAL_TYPE_BI) - #undef _GW_GROOVE_SERIAL - #define _GW_GROOVE_SERIAL serial2 + GWBASE_USE(M5_SERIAL_KIT_232) + CFG_SERIAL(BASE,1,BOARD_LEFT1,BOARD_LEFT2,GWSERIAL_TYPE_BI) + #define _GW_BASE_SERIAL #endif //M5 Serial (Atomic RS485 Base) #ifdef M5_SERIAL_KIT_485 - #ifdef _GWM5_BOARD - #error "can only define one M5 base" - #endif - #define _GWM5_BOARD - CFG_SERIAL(serial,BOARD_LEFT1,BOARD_LEFT2,GWSERIAL_TYPE_UNI) - #undef _GW_GROOVE_SERIAL - #define _GW_GROOVE_SERIAL serial2 + GWBASE_USE(M5_SERIAL_KIT_485) + CFG_SERIAL(BASE,1,BOARD_LEFT1,BOARD_LEFT2,GWSERIAL_TYPE_UNI) + #define _GW_BASE_SERIAL #endif -//M5 GPS (Atomic GPS Base) +//M5 GPS (Atomic GPS Base)#define GW_GETGROOVE(prfx,pin #ifdef M5_GPS_KIT - #ifdef _GWM5_BOARD - #error "can only define one M5 base" - #endif - #define _GWM5_BOARD - CFG_SERIAL(serial,BOARD_LEFT1,-1,GWSERIAL_TYPE_UNI) - #undef _GW_GROOVE_SERIAL - #define _GW_GROOVE_SERIAL serial2 + GWBASE_USE(M5_GPS_KIT) + CFG_SERIAL(BASE,1,BOARD_LEFT1,-1,GWSERIAL_TYPE_UNI) CFG_INIT(serialBaud,"9600",READONLY) + #define _GW_BASE_SERIAL #endif //M5 ProtoHub #ifdef M5_PROTO_HUB - #ifdef _GWM5_BOARD - #error "can only define one M5 base" - #endif - #define _GWM5_BOARD + GWBASE_USE(M5_PROTO_HUB) #define PPIN22 BOARD_LEFT1 #define PPIN19 BOARD_LEFT2 #define PPIN23 BOARD_LEFT3 @@ -199,18 +206,40 @@ //M5 PortABC extension #ifdef M5_PORTABC - #ifdef _GWM5_BOARD - #error "can only define one M5 base" - #endif - #define _GWM5_BOARD - #define ABC_PAYELLOW BOARD_RIGHT2 - #define ABC_PAWHITE BOARD_RIGHT1 - #define ABC_PBYELLOW BOARD_LEFT3 - #define ABC_PBWHITE BOARD_LEFT4 - #define ABC_PCYELLOW BOARD_LEFT1 - #define ABC_PCWHITE BOARD_LEFT2 + GWBASE_USE(M5_PORTABC) + //yellow pings are grove2 + GW_SETGROOVE(2,BOARD_RIGHT1,BOARD_RIGHT2) + GW_SETGROOVE(3,BOARD_LEFT4,BOARD_LEFT3) + GW_SETGROOVE(4,BOARD_LEFT2,BOARD_LEFT1) #endif +#ifndef GW_GROOVE_SERIAL + //GW_GROOVE_SERIAL can be 1,2 + #ifdef _GW_BASE_SERIAL + #define GW_GROOVE_SERIAL 2 + #else + #define GW_GROOVE_SERIAL 1 + #endif +#endif +#ifdef GW_GROOVE_SERIAL + GW_SETRES(serial,1,GW_GROOVE_SERIAL) +#endif +#ifdef GW_GROOVE2_SERIAL + GW_SETRES(serial,2,GW_GROOVE1_SERIAL) +#endif +#ifdef GW_GROOVE3_SERIAL + GW_SETRES(serial,3,GW_GROOVE2_SERIAL) +#endif +#ifdef GW_GROOVE4_SERIAL + GW_SETRES(serial,4,GW_GROOVE3_SERIAL) +#endif + +static constexpr const char * serial2cfg(const int serial){ + CASSERT(serial==1 || serial == 2,"invalid serial"); + return (serial==1)?"serial":(serial==2)?"serial2":""; +} + + //below we define the final device config based on the above //boards and peripherals //this allows us to easily also set them from outside @@ -218,35 +247,32 @@ //we use serial2 for groove serial if serial1 is already defined //before (e.g. by serial kit) #ifdef SERIAL_GROOVE_485 - GROVE_USE(SERIAL_GROOVE_485) - CFG_SERIAL(_GW_GROOVE_SERIAL,GROOVE_PIN_1,GROOVE_PIN_2,GWSERIAL_TYPE_UNI) + GWRESOURCE_USE(GROVE,SERIAL_GROOVE_485) + CFG_SERIAL(SERIAL_GROOVE_485,GW_GETRES(serial,SERIAL_GROOVE_485),GW_GETGROOVE(SERIAL_GROOVE_485,1),GW_GETGROOVE(SERIAL_GROOVE_485,2),GWSERIAL_TYPE_UNI) #endif #ifdef SERIAL_GROOVE_232 - GROVE_USE(SERIAL_GROOVE_232) - CFG_SERIAL(_GW_GROOVE_SERIAL,GROOVE_PIN_1,GROOVE_PIN_2,GWSERIAL_TYPE_BI) + GWRESOURCE_USE(GROVE,SERIAL_GROOVE_232) + CFG_SERIAL(SERIAL_GROOVE_232,GW_GETRES(serial,SERIAL_GROOVE_232),GW_GETGROOVE(SERIAL_GROOVE_232,1),GW_GETGROOVE(SERIAL_GROOVE_232,2),GWSERIAL_TYPE_BI) #endif //http://docs.m5stack.com/en/unit/gps #ifdef M5_GPS_UNIT - GROVE_USE(M5_GPS_UNIT) - CFG_SERIAL(_GW_GROOVE_SERIAL,GROOVE_PIN_1,-1,GWSERIAL_TYPE_RX) - CFG_INITP(_GW_GROOVE_SERIAL,Baud,"9600",READONLY) + GWRESOURCE_USE(GROVE,M5_GPS_UNIT) + CFG_SERIAL(M5_GPS_UNIT,GW_GETRES(serial,M5_GPS_UNIT),GW_GETGROOVE(M5_GPS_UNIT,1),-1,GWSERIAL_TYPE_RX) + CFG_INITP(GW_GETRES(serial,M5_GPS_UNIT),Baud,"9600",READONLY) #endif //can kit for M5 Atom #ifdef M5_CAN_KIT - #ifdef _GWM5_BOARD - #error "can only define one M5 base" - #endif - #define _GWM5_BOARD + GWBASE_USE(M5_CAN_KIT) #define ESP32_CAN_TX_PIN BOARD_LEFT1 #define ESP32_CAN_RX_PIN BOARD_LEFT2 #endif //CAN via groove #ifdef M5_CANUNIT - GROVE_USE(M5_CANUNIT) - #define ESP32_CAN_TX_PIN GROOVE_PIN_2 - #define ESP32_CAN_RX_PIN GROOVE_PIN_1 + GWRESOURCE_USE(GROVE,M5_CANUNIT) + #define ESP32_CAN_TX_PIN GW_GETGROOVE(M5_CANUNIT,2) + #define ESP32_CAN_RX_PIN GW_GETGROOVE(M5_CANUNIT,1) #endif #ifdef M5_ENV3 @@ -330,5 +356,4 @@ CFG_INIT(ledBrightness,"64",HIDDEN) #endif - #endif diff --git a/src/main.cpp b/src/main.cpp index 6a6005b..e87eab5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,6 +20,7 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting #include #include "Preferences.h" #include "GwApi.h" +#define GW_PINDEFS #include "GwHardware.h" #ifndef N2K_LOAD_LEVEL @@ -70,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"); From 1c80bf46b0785f367092d8d483e1aa92b12b7f23 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 20 Mar 2024 16:16:47 +0100 Subject: [PATCH 19/35] handle 4 groove port for serial and CAN --- lib/channel/GwChannelList.cpp | 57 +++++++-- lib/channel/GwChannelList.h | 3 + lib/hardware/GwHardware.h | 233 +++++++++++++++++++++------------- src/main.cpp | 1 + 4 files changed, 197 insertions(+), 97 deletions(-) diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp index 0fa8b52..bf7817a 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -1,19 +1,33 @@ #include "GwChannelList.h" #include "GwApi.h" - -using SerInitFunction=std::function; -std::vector initFunctions; - -#define CFG_SERIAL(id,ser,rx,tx,mode) \ - __MSG("serial config " #id __XSTR(ser) "(" #ser ")" " rx=" __XSTR(rx) ", tx=" __XSTR(tx) ",mode=" __STR(mode)); \ - static GwInitializer __serial ## id ## _init \ - (initFunctions,[](GwChannelList *cl){cl->addSerial(ser,rx,tx,mode);}); //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; @@ -205,7 +219,28 @@ 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) + } + } + } +} void GwChannelList::begin(bool fallbackSerial){ LOG_DEBUG(GwLog::DEBUG,"GwChannelList::begin"); GwChannel *channel=NULL; @@ -250,8 +285,8 @@ void GwChannelList::begin(bool fallbackSerial){ theChannels.push_back(channel); //new serial config handling - for (auto &&init:initFunctions){ - init(this); + for (auto &&init:serialInits){ + addSerial(init.serial,init.rx,init.tx,init.mode); } //handle separate defines //serial 1 diff --git a/lib/channel/GwChannelList.h b/lib/channel/GwChannelList.h index c8d3bda..ed11e06 100644 --- a/lib/channel/GwChannelList.h +++ b/lib/channel/GwChannelList.h @@ -37,6 +37,9 @@ class GwChannelList{ 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/hardware/GwHardware.h b/lib/hardware/GwHardware.h index d0f00b8..86c0114 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" @@ -24,43 +28,24 @@ #include #include "GwAppInfo.h" #include "GwUserTasks.h" -#ifndef CFG_INIT - #define CFG_INIT(...) -#endif -#define CFG_INITP(prefix,suffix,value,mode) CFG_INIT(prefix ## suffix,value,mode) -#ifndef CFG_SERIAL - #define CFG_SERIAL(...) -#endif #ifdef GW_PINDEFS #define GWRESOURCE_USE(RES,USER) \ - __MSG(__STR(RES) __STR(USER) " used by " #USER) \ - static int __EXPAND3(_resourceUsed,RES,USER) =1; - #define GWBASE_USE(USER) \ - __MSG("base unit " #USER) \ - static int _resourceUsedBase=1; - #define GW_SETGROOVE(prfx,p1,p2) \ - static int __EXPAND3(GROOVEPIN_,prfx,1) = p1; \ - static int __EXPAND3(GROOVEPIN_,prfx,2) = p2; - #define GW_GETGROOVE(prfx,pin) \ - __EXPAND3(GROOVEPIN_,prfx,pin) - #define GW_SETRES(res,groove,val) \ - static int __EXPAND3(_resource,res,groove) = val; - #define GW_GETRES(res,groove) \ - __EXPAND3(_resource,res,groove) + __MSG(#RES " used by " #USER) \ + static int _resourceUsed ## RES =1; #else - #define GW_SETGROOVE(...) - #define GW_GETGROOVE(...) - #define GW_SETRES(...) - #define GW_GETRES(...) #define GWRESOURCE_USE(...) - #define GWBASE_USE(...) -#endif +#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) #ifdef PLATFORM_BOARD_M5STACK_ATOM - GW_SETGROOVE(1,GPIO_NUM_32,GPIO_NUM_26) + #define GROOVE_PIN_2 GPIO_NUM_26 + #define GROOVE_PIN_1 GPIO_NUM_32 #define GWBUTTON_PIN GPIO_NUM_39 #define GWLED_FASTLED #define GWLED_TYPE SK6812 @@ -81,7 +66,8 @@ //general definitiones for M5AtomS3 #ifdef PLATFORM_BOARD_M5STACK_ATOMS3 - GW_SETGROOVE(1,GPIO_NUM_1,GPIO_NUM_2) + #define GROOVE_PIN_2 GPIO_NUM_2 + #define GROOVE_PIN_1 GPIO_NUM_1 #define GWBUTTON_PIN GPIO_NUM_41 #define GWLED_FASTLED #define GWLED_TYPE WS2812 @@ -100,7 +86,8 @@ #endif //M5Stick C #ifdef PLATFORM_BOARD_M5STICK_C - GW_SETGROOVE(1,GPIO_NUM_31,GPIO_NUM_32) + #define GROOVE_PIN_2 GPIO_NUM_32 + #define GROOVE_PIN_1 GPIO_NUM_31 #define USBSerial Serial #endif @@ -174,28 +161,27 @@ //M5 Serial (Atomic RS232 Base) #ifdef M5_SERIAL_KIT_232 - GWBASE_USE(M5_SERIAL_KIT_232) - CFG_SERIAL(BASE,1,BOARD_LEFT1,BOARD_LEFT2,GWSERIAL_TYPE_BI) - #define _GW_BASE_SERIAL + 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 - GWBASE_USE(M5_SERIAL_KIT_485) - CFG_SERIAL(BASE,1,BOARD_LEFT1,BOARD_LEFT2,GWSERIAL_TYPE_UNI) - #define _GW_BASE_SERIAL + 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)#define GW_GETGROOVE(prfx,pin +//M5 GPS (Atomic GPS Base) #ifdef M5_GPS_KIT - GWBASE_USE(M5_GPS_KIT) - CFG_SERIAL(BASE,1,BOARD_LEFT1,-1,GWSERIAL_TYPE_UNI) - CFG_INIT(serialBaud,"9600",READONLY) - #define _GW_BASE_SERIAL + 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 - GWBASE_USE(M5_PROTO_HUB) + GWRESOURCE_USE(BASE,M5_PROTO_HUB) #define PPIN22 BOARD_LEFT1 #define PPIN19 BOARD_LEFT2 #define PPIN23 BOARD_LEFT3 @@ -206,40 +192,15 @@ //M5 PortABC extension #ifdef M5_PORTABC - GWBASE_USE(M5_PORTABC) - //yellow pings are grove2 - GW_SETGROOVE(2,BOARD_RIGHT1,BOARD_RIGHT2) - GW_SETGROOVE(3,BOARD_LEFT4,BOARD_LEFT3) - GW_SETGROOVE(4,BOARD_LEFT2,BOARD_LEFT1) + 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 -#ifndef GW_GROOVE_SERIAL - //GW_GROOVE_SERIAL can be 1,2 - #ifdef _GW_BASE_SERIAL - #define GW_GROOVE_SERIAL 2 - #else - #define GW_GROOVE_SERIAL 1 - #endif -#endif -#ifdef GW_GROOVE_SERIAL - GW_SETRES(serial,1,GW_GROOVE_SERIAL) -#endif -#ifdef GW_GROOVE2_SERIAL - GW_SETRES(serial,2,GW_GROOVE1_SERIAL) -#endif -#ifdef GW_GROOVE3_SERIAL - GW_SETRES(serial,3,GW_GROOVE2_SERIAL) -#endif -#ifdef GW_GROOVE4_SERIAL - GW_SETRES(serial,4,GW_GROOVE3_SERIAL) -#endif - -static constexpr const char * serial2cfg(const int serial){ - CASSERT(serial==1 || serial == 2,"invalid serial"); - return (serial==1)?"serial":(serial==2)?"serial2":""; -} - - //below we define the final device config based on the above //boards and peripherals //this allows us to easily also set them from outside @@ -247,34 +208,97 @@ static constexpr const char * serial2cfg(const int serial){ //we use serial2 for groove serial if serial1 is already defined //before (e.g. by serial kit) #ifdef SERIAL_GROOVE_485 - GWRESOURCE_USE(GROVE,SERIAL_GROOVE_485) - CFG_SERIAL(SERIAL_GROOVE_485,GW_GETRES(serial,SERIAL_GROOVE_485),GW_GETGROOVE(SERIAL_GROOVE_485,1),GW_GETGROOVE(SERIAL_GROOVE_485,2),GWSERIAL_TYPE_UNI) + GWRESOURCE_USE(GROOVE,SERIAL_GROOVE_485) + #define _GWI_SERIAL_GROOVE GWSERIAL_TYPE_UNI +#endif +#ifdef SERIAL_GROOVE_485_A + GWRESOURCE_USE(GROOVEA,SERIAL_GROOVE_485_A) + #define _GWI_SERIAL_GROOVE_A GWSERIAL_TYPE_UNI +#endif +#ifdef SERIAL_GROOVE_485_B + GWRESOURCE_USE(GROOVEB,SERIAL_GROOVE_485_B) + #define _GWI_SERIAL_GROOVE_B GWSERIAL_TYPE_UNI +#endif +#ifdef SERIAL_GROOVE_485_C + GWRESOURCE_USE(GROOVEC,SERIAL_GROOVE_485_C) + #define _GWI_SERIAL_GROOVE_C GWSERIAL_TYPE_UNI #endif #ifdef SERIAL_GROOVE_232 - GWRESOURCE_USE(GROVE,SERIAL_GROOVE_232) - CFG_SERIAL(SERIAL_GROOVE_232,GW_GETRES(serial,SERIAL_GROOVE_232),GW_GETGROOVE(SERIAL_GROOVE_232,1),GW_GETGROOVE(SERIAL_GROOVE_232,2),GWSERIAL_TYPE_BI) + GWRESOURCE_USE(GROOVE,SERIAL_GROOVE_232) + #define _GWI_SERIAL_GROOVE GWSERIAL_TYPE_BI #endif +#ifdef SERIAL_GROOVE_232_A + GWRESOURCE_USE(GROOVEA,SERIAL_GROOVE_232_A) + #define _GWI_SERIAL_GROOVE_A GWSERIAL_TYPE_BI +#endif +#ifdef SERIAL_GROOVE_232_B + GWRESOURCE_USE(GROOVEB,SERIAL_GROOVE_232_B) + #define _GWI_SERIAL_GROOVE_B GWSERIAL_TYPE_BI +#endif +#ifdef SERIAL_GROOVE_232_C + GWRESOURCE_USE(GROOVEC,SERIAL_GROOVE_232_C) + #define _GWI_SERIAL_GROOVE_C GWSERIAL_TYPE_BI +#endif + + + //http://docs.m5stack.com/en/unit/gps #ifdef M5_GPS_UNIT - GWRESOURCE_USE(GROVE,M5_GPS_UNIT) - CFG_SERIAL(M5_GPS_UNIT,GW_GETRES(serial,M5_GPS_UNIT),GW_GETGROOVE(M5_GPS_UNIT,1),-1,GWSERIAL_TYPE_RX) - CFG_INITP(GW_GETRES(serial,M5_GPS_UNIT),Baud,"9600",READONLY) + GWRESOURCE_USE(GROOVE,M5_GPS_UNIT) + #define _GWI_SERIAL_GROOVE GWSERIAL_TYPE_RX,"9600" #endif +#ifdef M5_GPS_UNIT_A + GWRESOURCE_USE(GROOVEA,M5_GPS_UNIT_A) + #define _GWI_SERIAL_GROOVE_A GWSERIAL_TYPE_RX,"9600" +#endif +#ifdef M5_GPS_UNIT_B + GWRESOURCE_USE(GROOVEB,M5_GPS_UNIT_B) + #define _GWI_SERIAL_GROOVE_B GWSERIAL_TYPE_RX,"9600" +#endif +#ifdef M5_GPS_UNIT_C + GWRESOURCE_USE(GROOVEC,M5_GPS_UNIT) + #define _GWI_SERIAL_GROOVE_C GWSERIAL_TYPE_RX,"9600" +#endif + + + //can kit for M5 Atom #ifdef M5_CAN_KIT - GWBASE_USE(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 //CAN via groove #ifdef M5_CANUNIT - GWRESOURCE_USE(GROVE,M5_CANUNIT) - #define ESP32_CAN_TX_PIN GW_GETGROOVE(M5_CANUNIT,2) - #define ESP32_CAN_RX_PIN GW_GETGROOVE(M5_CANUNIT,1) + GWRESOURCE_USE(GROOVE,M5_CANUNIT) + GWRESOURCE_USE(CAN,M5_CANUNIT) + #define ESP32_CAN_TX_PIN GROOVE_PIN_2 + #define ESP32_CAN_RX_PIN GROOVE_PIN_1 #endif +#ifdef M5_CANUNIT_A + GWRESOURCE_USE(GROOVEA,M5_CANUNIT_A) + GWRESOURCE_USE(CAN,M5_CANUNIT_A) + #define ESP32_CAN_TX_PIN GROOVEA_PIN_2 + #define ESP32_CAN_RX_PIN GROOVEA_PIN_1 +#endif +#ifdef M5_CANUNIT_B + GWRESOURCE_USE(GROOVEB,M5_CANUNIT_B) + GWRESOURCE_USE(CAN,M5_CANUNIT_B) + #define ESP32_CAN_TX_PIN GROOVEB_PIN_2 + #define ESP32_CAN_RX_PIN GROOVEA_PIN_1 +#endif +#ifdef M5_CANUNIT_C + GWRESOURCE_USE(GROOVEC,M5_CANUNIT_C) + GWRESOURCE_USE(CAN,M5_CANUNIT_C) + #define ESP32_CAN_TX_PIN GROOVEC_PIN_2 + #define ESP32_CAN_RX_PIN GROOVEC_PIN_1 +#endif + + #ifdef M5_ENV3 #ifndef M5_GROOVEIIC #define M5_GROOVEIIC M5_ENV3 @@ -288,7 +312,7 @@ static constexpr const char * serial2cfg(const int serial){ #endif #ifdef M5_GROOVEIIC - GROVE_USE(M5_GROOVEIIC) + GROOVE_USE(M5_GROOVEIIC) #ifdef GWIIC_SCL #error "you cannot define both GWIIC_SCL and M5_GROOVEIIC" #endif @@ -299,6 +323,43 @@ static constexpr const char * serial2cfg(const int serial){ #define GWIIC_SDA GROOVE_PIN_2 #endif +#ifdef _GWI_SERIAL_GROOVE + #ifndef _GWI_SERIAL1 + #define _GWI_SERIAL1 GROOVE_PIN_1,GROOVE_PIN_2,_GWI_SERIAL_GROOVE + #elif ! defined(_GWI_SERIAL2) + #define _GWI_SERIAL2 GROOVE_PIN_1,GROOVE_PIN_2,_GWI_SERIAL_GROOVE + #else + #error "both serial devices already in use" + #endif +#endif +#ifdef _GWI_SERIAL_GROOVE_A + #ifndef _GWI_SERIAL1 + #define _GWI_SERIAL1 GROOVEA_PIN_1,GROOVEA_PIN_2,_GWI_SERIAL_GROOVE_A + #elif ! defined(_GWI_SERIAL2) + #define _GWI_SERIAL2 GROOVEA_PIN_1,GROOVEA_PIN_2,_GWI_SERIAL_GROOVE_A + #else + #error "both serial devices already in use" + #endif +#endif +#ifdef _GWI_SERIAL_GROOVE_B + #ifndef _GWI_SERIAL1 + #define _GWI_SERIAL1 GROOVEB_PIN_1,GROOVEB_PIN_2,_GWI_SERIAL_GROOVE_B + #elif ! defined(_GWI_SERIAL2) + #define _GWI_SERIAL2 GROOVEB_PIN_1,GROOVEB_PIN_2,_GWI_SERIAL_GROOVE_B + #else + #error "both serial devices already in use" + #endif +#endif +#ifdef _GWI_SERIAL_GROOVE_C + #ifndef _GWI_SERIAL1 + #define _GWI_SERIAL1 GROOVEC_PIN_1,GROOVEC_PIN_2,_GWI_SERIAL_GROOVE_C + #elif ! defined(_GWI_SERIAL2) + #define _GWI_SERIAL2 GROOVEC_PIN_1,GROOVEC_PIN_2,_GWI_SERIAL_GROOVE_C + #else + #error "both serial devices already in use" + #endif +#endif + #ifdef GWIIC_SDA #ifndef GWIIC_SCL #error "you must both define GWIIC_SDA and GWIIC_SCL" diff --git a/src/main.cpp b/src/main.cpp index e87eab5..672fc7d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -784,6 +784,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); From e806971713f31b1d37013d3f57bb690eb00ecb95 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 20 Mar 2024 17:43:19 +0100 Subject: [PATCH 20/35] multiple groves for iic #1 --- lib/hardware/GwHardware.h | 80 ++++++++++++++++++++++++++++++------ lib/iictask/GwIicSensors.h | 17 ++++++++ lib/iictask/GwIicTask.cpp | 84 ++++++++++++++++++-------------------- lib/iictask/GwSHT3X.cpp | 32 +++++++++++++++ lib/iictask/GwSHT3X.h | 2 - 5 files changed, 157 insertions(+), 58 deletions(-) diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 86c0114..c80cb57 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -40,6 +40,9 @@ #ifndef CFG_INIT #define CFG_INIT(...) #endif +#ifndef GROOVE_IIC + #define GROOVE_IIC(...) +#endif //general definitions for M5AtomLite //hint for groove pins: //according to some schematics the numbering is 1,2,3(VCC),4(GND) @@ -301,26 +304,79 @@ #ifdef M5_ENV3 #ifndef M5_GROOVEIIC - #define M5_GROOVEIIC M5_ENV3 + #define M5_GROOVEIIC #endif - #ifndef GWSHT3X - #define GWSHT3X -1 + GROOVE_IIC(GWSHT3X,Z,1) + GROOVE_IIC(GWQMP6988,Z,1) + #define _GWSHT3X +#endif +#ifdef M5_ENV3_A + #ifndef M5_GROOVEIIC_A + #define M5_GROOVEIIC_A #endif - #ifndef GWQMP6988 - #define GWQMP6988 -1 + GROOVE_IIC(GWSHT3X,A,1) + GROOVE_IIC(GWQMP6988,A,1) + #define _GWSHT3X +#endif +#ifdef M5_ENV3_B + #ifndef M5_GROOVEIIC_B + #define M5_GROOVEIIC_B #endif + GROOVE_IIC(GWSHT3X,B,1) + GROOVE_IIC(GWQMP6988,B,1) + #define _GWSHT3X +#endif +#ifdef M5_ENV3_C + #ifndef M5_GROOVEIIC_C + #define M5_GROOVEIIC_C + #endif + GROOVE_IIC(GWSHT3X,C,1) + GROOVE_IIC(GWQMP6988,C,1) + #define _GWSHT3X #endif #ifdef M5_GROOVEIIC - GROOVE_USE(M5_GROOVEIIC) - #ifdef GWIIC_SCL - #error "you cannot define both GWIIC_SCL and M5_GROOVEIIC" + GWRESOURCE_USE(GROOVE,M5_GROOVEIIC) + #ifndef _GWI_IIC1 + #define _GWI_IIC1 "Z",GROOVE_PIN_1,GROOVE_PIN_2 + #elif ! defined(_GWI_IIC2) + #define _GWI_IIC2 "Z",GROOVE_PIN_1,GROOVE_PIN_2 + #else + #error "both iic buses already in use" #endif - #define GWIIC_SCL GROOVE_PIN_1 - #ifdef GWIIC_SDA - #error "you cannot define both GWIIC_SDA and M5_GROOVEIIC" +#endif +#ifdef M5_GROOVEIIC_A + GWRESOURCE_USE(GROOVA,M5_GROOVEIIC_A) + #ifndef _GWI_IIC1 + #define _GWI_IIC1 A,GROOVEA_PIN_1,GROOVEA_PIN_2 + #elif ! defined(_GWI_IIC2) + #define _GWI_IIC2 A,GROOVEA_PIN_1,GROOVEA_PIN_2 + #else + #error "both iic buses already in use" #endif - #define GWIIC_SDA GROOVE_PIN_2 +#endif +#ifdef M5_GROOVEIIC_B + GWRESOURCE_USE(GROOVB,M5_GROOVEIIC_B) + #ifndef _GWI_IIC1 + #define _GWI_IIC1 B,GROOVEB_PIN_1,GROOVEB_PIN_2 + #elif ! defined(_GWI_IIC2) + #define _GWI_IIC2 B,GROOVEB_PIN_1,GROOVEB_PIN_2 + #else + #error "both iic buses already in use" + #endif +#endif +#ifdef M5_GROOVEIIC_C + GWRESOURCE_USE(GROOVC,M5_GROOVEIIC_C) + #ifndef _GWI_IIC1 + #define _GWI_IIC1 C,GROOVEC_PIN_1,GROOVEC_PIN_2 + #elif ! defined(_GWI_IIC2) + #define _GWI_IIC2 C,GROOVEC_PIN_1,GROOVEC_PIN_2 + #else + #error "both iic buses already in use" + #endif +#endif +#if defined(_GWI_IIC1) || defined (_GWI_IIC2) + #define _GWIIC #endif #ifdef _GWI_SERIAL_GROOVE diff --git a/lib/iictask/GwIicSensors.h b/lib/iictask/GwIicSensors.h index c3d21b4..dda38b9 100644 --- a/lib/iictask/GwIicSensors.h +++ b/lib/iictask/GwIicSensors.h @@ -3,6 +3,23 @@ #include "GwApi.h" #include "N2kMessages.h" #include "GwXdrTypeMappings.h" +#ifdef _IIC_GROOVE_LIST + 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)); +#endif #include "GwHardware.h" #include "GwSensor.h" #ifdef _GWIIC diff --git a/lib/iictask/GwIicTask.cpp b/lib/iictask/GwIicTask.cpp index 9698592..a9982ec 100644 --- a/lib/iictask/GwIicTask.cpp +++ b/lib/iictask/GwIicTask.cpp @@ -6,6 +6,9 @@ #include "GwSHT3X.h" #include +#include "GwTimer.h" +#include "GwHardware.h" + #ifndef GWIIC_SDA #define GWIIC_SDA -1 #endif @@ -19,11 +22,6 @@ #define GWIIC_SCL2 -1 #endif -#include "GwTimer.h" -#include "GwHardware.h" - - - void runIicTask(GwApi *api); static IICSensorList sensors; @@ -54,6 +52,39 @@ 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); + } +} void runIicTask(GwApi *api){ GwLog *logger=api->getLogger(); std::map buses; @@ -66,50 +97,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 - { - buses[busId] = &Wire; - LOG_DEBUG(GwLog::ERROR, "initialized IIC 1 at sda=%d,scl=%d", - (int)GWIIC_SDA, (int)GWIIC_SCL); - } + if (initWire(logger,Wire,1)){ + buses[busId] = &Wire; } } 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 - { - buses[busId] = &Wire1; - LOG_DEBUG(GwLog::LOG, "initialized IIC 2 at sda=%d,scl=%d", - (int)GWIIC_SDA2, (int)GWIIC_SCL2); - } + if (initWire(logger,Wire1,2)){ + buses[busId] = &Wire1; } } break; diff --git a/lib/iictask/GwSHT3X.cpp b/lib/iictask/GwSHT3X.cpp index 8305220..705012a 100644 --- a/lib/iictask/GwSHT3X.cpp +++ b/lib/iictask/GwSHT3X.cpp @@ -1,3 +1,4 @@ +#define _IIC_GROOVE_LIST #include "GwSHT3X.h" #ifdef _GWSHT3X @@ -5,6 +6,8 @@ #define PRFX2 "SHT3X12" #define PRFX3 "SHT3X21" #define PRFX4 "SHT3X22" +static void addGroveItems(GwApi *api, IICSensorList &sensors, const String &bus,const String &grove, int, int); + class SHT3XConfig : public IICSensorBase{ public: String tmNam; @@ -77,6 +80,7 @@ class SHT3XConfig : public IICSensorBase{ CFG_GET(tmSrc,prefix); virtual void readConfig(GwConfigHandler *cfg){ + if (ok) return; if (prefix == PRFX1){ busId=1; addr=0x44; @@ -138,6 +142,34 @@ void registerSHT3X(GwApi *api,IICSensorList &sensors){ #pragma message "GWSHT3X22 defined" } #endif + #ifdef _GWI_IIC1 + addGroveItems(api,sensors,"1",_GWI_IIC1); + #endif + #ifdef _GWI_IIC2 + addGroveItems(api,sensors,"2",_GWI_IIC2); + #endif +}; +static void addGroveItems(GwApi *api, IICSensorList &sensors, const String &bus,const String &grove, int, int) +{ + GwLog *logger=api->getLogger(); + for (auto &&init : iicGroveList) + { + String prfx = init.item(grove, bus); + if (!prfx.isEmpty()) + { + SHT3XConfig *scfg = new SHT3XConfig(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); + } + else + { + LOG_DEBUG(GwLog::ERROR, "invalid grove sensor config %s", prfx.c_str()); + } + } + } } #else void registerSHT3X(GwApi *api,IICSensorList &sensors){ diff --git a/lib/iictask/GwSHT3X.h b/lib/iictask/GwSHT3X.h index 263d23e..b1926af 100644 --- a/lib/iictask/GwSHT3X.h +++ b/lib/iictask/GwSHT3X.h @@ -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 From 99d7484afb9b219d3fd1ed45c71b5336a64e90a0 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 20 Mar 2024 18:30:40 +0100 Subject: [PATCH 21/35] handle multiple groves for iic --- lib/hardware/GwHardware.h | 20 +++++++----- lib/iictask/GwIicSensors.h | 17 ---------- lib/iictask/GwIicTask.cpp | 64 ++++++++++++++++++++++++++++++++++++-- lib/iictask/GwQMP6988.cpp | 12 +++++-- lib/iictask/GwQMP6988.h | 4 +-- lib/iictask/GwSHT3X.cpp | 48 +++++++--------------------- lib/iictask/GwSHT3X.h | 2 +- lib/sensors/GwSensor.h | 2 ++ 8 files changed, 98 insertions(+), 71 deletions(-) diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index c80cb57..5188212 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -306,33 +306,37 @@ #ifndef M5_GROOVEIIC #define M5_GROOVEIIC #endif - GROOVE_IIC(GWSHT3X,Z,1) - GROOVE_IIC(GWQMP6988,Z,1) + GROOVE_IIC(SHT3X,Z,1) + GROOVE_IIC(QMP6988,Z,1) #define _GWSHT3X + #define _GWQMP6988 #endif #ifdef M5_ENV3_A #ifndef M5_GROOVEIIC_A #define M5_GROOVEIIC_A #endif - GROOVE_IIC(GWSHT3X,A,1) - GROOVE_IIC(GWQMP6988,A,1) + GROOVE_IIC(SHT3X,A,1) + GROOVE_IIC(QMP6988,A,1) #define _GWSHT3X + #define _GWQMP6988 #endif #ifdef M5_ENV3_B #ifndef M5_GROOVEIIC_B #define M5_GROOVEIIC_B #endif - GROOVE_IIC(GWSHT3X,B,1) - GROOVE_IIC(GWQMP6988,B,1) + GROOVE_IIC(SHT3X,B,1) + GROOVE_IIC(QMP6988,B,1) #define _GWSHT3X + #define _GWQMP6988 #endif #ifdef M5_ENV3_C #ifndef M5_GROOVEIIC_C #define M5_GROOVEIIC_C #endif - GROOVE_IIC(GWSHT3X,C,1) - GROOVE_IIC(GWQMP6988,C,1) + GROOVE_IIC(SHT3X,C,1) + GROOVE_IIC(QMP6988,C,1) #define _GWSHT3X + #define _GWQMP6988 #endif #ifdef M5_GROOVEIIC diff --git a/lib/iictask/GwIicSensors.h b/lib/iictask/GwIicSensors.h index dda38b9..c3d21b4 100644 --- a/lib/iictask/GwIicSensors.h +++ b/lib/iictask/GwIicSensors.h @@ -3,23 +3,6 @@ #include "GwApi.h" #include "N2kMessages.h" #include "GwXdrTypeMappings.h" -#ifdef _IIC_GROOVE_LIST - 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)); -#endif #include "GwHardware.h" #include "GwSensor.h" #ifdef _GWIIC diff --git a/lib/iictask/GwIicTask.cpp b/lib/iictask/GwIicTask.cpp index a9982ec..ea39c0f 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" @@ -7,7 +26,6 @@ #include #include "GwTimer.h" -#include "GwHardware.h" #ifndef GWIIC_SDA #define GWIIC_SDA -1 @@ -25,6 +43,33 @@ void runIicTask(GwApi *api); static IICSensorList sensors; +static void addGroveItems(IICSensorBase::Creator creator,GwApi *api, IICSensorList &sensors, const String &bus,const String &grove, int, int) +{ + GwLog *logger=api->getLogger(); + for (auto &&init : iicGroveList) + { + LOG_DEBUG(GwLog::LOG, "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()) + { + 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); + } + else + { + LOG_DEBUG(GwLog::ERROR, "invalid grove sensor config %s", prfx.c_str()); + delete scfg; + } + } + } +} void initIicTask(GwApi *api){ GwLog *logger=api->getLogger(); @@ -33,8 +78,20 @@ void initIicTask(GwApi *api){ #else bool addTask=false; GwConfigHandler *config=api->getConfig(); - registerSHT3X(api,sensors); - registerQMP6988(api,sensors); + IICSensorBase::Creator sht3xCreator=registerSHT3X(api,sensors); + #ifdef _GWI_IIC1 + addGroveItems(sht3xCreator,api,sensors,"1",_GWI_IIC1); + #endif + #ifdef _GWI_IIC2 + addGroveItems(sht3xCreator,api,sensors,"2",_GWI_IIC2); + #endif + IICSensorBase::Creator qmp6988Creator=registerQMP6988(api,sensors); + #ifdef _GWI_IIC1 + addGroveItems(qmp6988Creator,api,sensors,"1",_GWI_IIC1); + #endif + #ifdef _GWI_IIC2 + addGroveItems(qmp6988Creator,api,sensors,"2",_GWI_IIC2); + #endif registerBME280(api,sensors); for (auto it=sensors.begin();it != sensors.end();it++){ if ((*it)->preinit(api)) addTask=true; @@ -84,6 +141,7 @@ bool initWire(GwLog *logger, TwoWire &wire, int num){ #endif return initWireDo(logger,wire,num,"",GWIIC_SDA2,GWIIC_SCL2); } + return false; } void runIicTask(GwApi *api){ GwLog *logger=api->getLogger(); diff --git a/lib/iictask/GwQMP6988.cpp b/lib/iictask/GwQMP6988.cpp index 638bf0e..0dc8ebd 100644 --- a/lib/iictask/GwQMP6988.cpp +++ b/lib/iictask/GwQMP6988.cpp @@ -1,3 +1,4 @@ +#define _IIC_GROOVE_LIST #include "GwQMP6988.h" #ifdef _GWQMP6988 #define PRFX1 "QMP698811" @@ -48,6 +49,7 @@ class QMP6988Config : public IICSensorBase{ CFG_GET(prOff,prefix); virtual void readConfig(GwConfigHandler *cfg){ + if (ok) return; if (prefix == PRFX1){ busId=1; addr=86; @@ -76,7 +78,10 @@ class QMP6988Config : public IICSensorBase{ } }; -void registerQMP6988(GwApi *api,IICSensorList &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 +115,11 @@ void registerQMP6988(GwApi *api,IICSensorList &sensors){ #pragma message "GWQMP698822 defined" } #endif + return creator; } #else - void registerQMP6988(GwApi *api,IICSensorList &sensors){} + IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors){ + return IICSensorBase::Creator(); + } #endif \ No newline at end of file diff --git a/lib/iictask/GwQMP6988.h b/lib/iictask/GwQMP6988.h index 5d64454..cb6a881 100644 --- a/lib/iictask/GwQMP6988.h +++ b/lib/iictask/GwQMP6988.h @@ -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,IICSensorList &sensors); +IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors); #endif \ No newline at end of file diff --git a/lib/iictask/GwSHT3X.cpp b/lib/iictask/GwSHT3X.cpp index 705012a..9a6f3df 100644 --- a/lib/iictask/GwSHT3X.cpp +++ b/lib/iictask/GwSHT3X.cpp @@ -1,12 +1,9 @@ -#define _IIC_GROOVE_LIST #include "GwSHT3X.h" - #ifdef _GWSHT3X #define PRFX1 "SHT3X11" #define PRFX2 "SHT3X12" #define PRFX3 "SHT3X21" #define PRFX4 "SHT3X22" -static void addGroveItems(GwApi *api, IICSensorList &sensors, const String &bus,const String &grove, int, int); class SHT3XConfig : public IICSensorBase{ public: @@ -108,11 +105,14 @@ class SHT3XConfig : public IICSensorBase{ intv*=1000; } }; -void registerSHT3X(GwApi *api,IICSensorList &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" @@ -120,7 +120,7 @@ void registerSHT3X(GwApi *api,IICSensorList &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" @@ -128,7 +128,7 @@ void registerSHT3X(GwApi *api,IICSensorList &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" @@ -136,43 +136,17 @@ void registerSHT3X(GwApi *api,IICSensorList &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 - #ifdef _GWI_IIC1 - addGroveItems(api,sensors,"1",_GWI_IIC1); - #endif - #ifdef _GWI_IIC2 - addGroveItems(api,sensors,"2",_GWI_IIC2); - #endif + return creator; }; -static void addGroveItems(GwApi *api, IICSensorList &sensors, const String &bus,const String &grove, int, int) -{ - GwLog *logger=api->getLogger(); - for (auto &&init : iicGroveList) - { - String prfx = init.item(grove, bus); - if (!prfx.isEmpty()) - { - SHT3XConfig *scfg = new SHT3XConfig(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); - } - else - { - LOG_DEBUG(GwLog::ERROR, "invalid grove sensor config %s", prfx.c_str()); - } - } - } -} + #else -void registerSHT3X(GwApi *api,IICSensorList &sensors){ +IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors){ } diff --git a/lib/iictask/GwSHT3X.h b/lib/iictask/GwSHT3X.h index b1926af..f59d51f 100644 --- a/lib/iictask/GwSHT3X.h +++ b/lib/iictask/GwSHT3X.h @@ -16,5 +16,5 @@ #ifdef _GWSHT3X #include "SHT3X.h" #endif -void registerSHT3X(GwApi *api,IICSensorList &sensors); +IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors); #endif \ No newline at end of file diff --git a/lib/sensors/GwSensor.h b/lib/sensors/GwSensor.h index e33aef3..e336163 100644 --- a/lib/sensors/GwSensor.h +++ b/lib/sensors/GwSensor.h @@ -28,6 +28,7 @@ class SensorBase{ virtual void readConfig(GwConfigHandler *cfg)=0; SensorBase(GwApi *api,const String &prfx):prefix(prfx){ } + using Creator=std::function *(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;} @@ -46,6 +47,7 @@ class SensorList : public std::vector*>{ using std::vector*>::vector; }; + #define CFG_GET(name,prefix) \ cfg->getValue(name, GwConfigDefinitions::prefix ## name) From b091d9aee4bc6dbd1106c1afaf4109c6b2f0d917 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 21 Mar 2024 11:51:10 +0100 Subject: [PATCH 22/35] correct handling for grove based iic sensors --- lib/iictask/GwBME280.cpp | 32 ++++++++++++++----------- lib/iictask/GwBME280.h | 2 +- lib/iictask/GwIicSensors.h | 13 +++++++--- lib/iictask/GwIicTask.cpp | 49 +++++++++++++++++++++----------------- lib/iictask/GwQMP6988.cpp | 11 +++++---- lib/iictask/GwSHT3X.cpp | 13 +++++----- lib/sensors/GwSensor.h | 5 ++-- lib/spitask/GwSpiSensor.h | 2 +- 8 files changed, 73 insertions(+), 54 deletions(-) diff --git a/lib/iictask/GwBME280.cpp b/lib/iictask/GwBME280.cpp index 498bf1e..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,10 +15,11 @@ #include #endif #ifdef _GWBME280 -#define PRFX1 "BME28011" -#define PRFX2 "BME28012" -#define PRFX3 "BME28021" -#define PRFX4 "BME28022" +#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; @@ -36,7 +35,7 @@ class BME280Config : public IICSensorBase{ 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 IICSensorBase{ virtual void readConfig(GwConfigHandler *cfg) override { + if (ok) return; if (prefix == PRFX1) { busId = 1; @@ -137,11 +137,13 @@ class BME280Config : public IICSensorBase{ } }; - -void registerBME280(GwApi *api,IICSensorList &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,IICSensorList &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,IICSensorList &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,IICSensorList &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,IICSensorList &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 5f2abb5..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,IICSensorList &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 c3d21b4..dcb7f3f 100644 --- a/lib/iictask/GwIicSensors.h +++ b/lib/iictask/GwIicSensors.h @@ -103,8 +103,15 @@ void sendN2kTemperature(GwApi *api,CFG &cfg,double value, int counterId){ } - -#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 ea39c0f..cf88d01 100644 --- a/lib/iictask/GwIicTask.cpp +++ b/lib/iictask/GwIicTask.cpp @@ -43,29 +43,39 @@ static std::vector iicGroveList; void runIicTask(GwApi *api); static IICSensorList sensors; -static void addGroveItems(IICSensorBase::Creator creator,GwApi *api, IICSensorList &sensors, const String &bus,const String &grove, int, int) +static void addGroveItems(std::vector &creators,GwApi *api, IICSensorList &sensors, const String &bus,const String &grove, int, int) { GwLog *logger=api->getLogger(); for (auto &&init : iicGroveList) { - LOG_DEBUG(GwLog::LOG, "trying grove item %s:%s:%s for grove %s, bus %s", + 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()) { - auto *scfg = creator(api, prfx); - scfg->readConfig(api->getConfig()); - if (scfg->ok) + bool found=false; + for (auto &&creator : creators) { - LOG_DEBUG(GwLog::LOG, "adding %s from grove config", prfx.c_str()); - sensors.add(api, scfg); + 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; + } } - else - { - LOG_DEBUG(GwLog::ERROR, "invalid grove sensor config %s", prfx.c_str()); - delete scfg; + if (! found){ + LOG_DEBUG(GwLog::ERROR,"no iic sensor found for %s",prfx.c_str()); } } } @@ -78,21 +88,16 @@ void initIicTask(GwApi *api){ #else bool addTask=false; GwConfigHandler *config=api->getConfig(); - IICSensorBase::Creator sht3xCreator=registerSHT3X(api,sensors); + std::vector creators; + creators.push_back(registerSHT3X(api,sensors)); + creators.push_back(registerQMP6988(api,sensors)); + creators.push_back(registerBME280(api,sensors)); #ifdef _GWI_IIC1 - addGroveItems(sht3xCreator,api,sensors,"1",_GWI_IIC1); + addGroveItems(creators,api,sensors,"1",_GWI_IIC1); #endif #ifdef _GWI_IIC2 - addGroveItems(sht3xCreator,api,sensors,"2",_GWI_IIC2); + addGroveItems(creators,api,sensors,"2",_GWI_IIC2); #endif - IICSensorBase::Creator qmp6988Creator=registerQMP6988(api,sensors); - #ifdef _GWI_IIC1 - addGroveItems(qmp6988Creator,api,sensors,"1",_GWI_IIC1); - #endif - #ifdef _GWI_IIC2 - addGroveItems(qmp6988Creator,api,sensors,"2",_GWI_IIC2); - #endif - registerBME280(api,sensors); for (auto it=sensors.begin();it != sensors.end();it++){ if ((*it)->preinit(api)) addTask=true; } diff --git a/lib/iictask/GwQMP6988.cpp b/lib/iictask/GwQMP6988.cpp index 0dc8ebd..f3a7671 100644 --- a/lib/iictask/GwQMP6988.cpp +++ b/lib/iictask/GwQMP6988.cpp @@ -1,10 +1,11 @@ #define _IIC_GROOVE_LIST #include "GwQMP6988.h" #ifdef _GWQMP6988 -#define PRFX1 "QMP698811" -#define PRFX2 "QMP698812" -#define PRFX3 "QMP698821" -#define PRFX4 "QMP698822" +#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"; @@ -12,7 +13,7 @@ class QMP6988Config : public IICSensorBase{ 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; diff --git a/lib/iictask/GwSHT3X.cpp b/lib/iictask/GwSHT3X.cpp index 9a6f3df..40735e1 100644 --- a/lib/iictask/GwSHT3X.cpp +++ b/lib/iictask/GwSHT3X.cpp @@ -1,9 +1,10 @@ #include "GwSHT3X.h" #ifdef _GWSHT3X -#define PRFX1 "SHT3X11" -#define PRFX2 "SHT3X12" -#define PRFX3 "SHT3X21" -#define PRFX4 "SHT3X22" +#define TYPE "SHT3X" +#define PRFX1 TYPE "11" +#define PRFX2 TYPE "12" +#define PRFX3 TYPE "21" +#define PRFX4 TYPE "22" class SHT3XConfig : public IICSensorBase{ public: @@ -15,7 +16,7 @@ class SHT3XConfig : public IICSensorBase{ tN2kTempSource tmSrc; SHT3X *device=nullptr; SHT3XConfig(GwApi *api,const String &prefix): - SensorBase(api,prefix){} + SensorBase(TYPE,api,prefix){} virtual bool isActive(){ return tmAct || huAct; } @@ -147,7 +148,7 @@ IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors){ #else IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors){ - + return IICSensorBase::Creator(); } #endif diff --git a/lib/sensors/GwSensor.h b/lib/sensors/GwSensor.h index e336163..da9b560 100644 --- a/lib/sensors/GwSensor.h +++ b/lib/sensors/GwSensor.h @@ -22,11 +22,12 @@ class SensorBase{ int busId=0; int iid=99; //N2K instanceId int addr=-1; - String prefix; + const String prefix; + const String type; long intv=0; bool ok=false; virtual void readConfig(GwConfigHandler *cfg)=0; - SensorBase(GwApi *api,const String &prfx):prefix(prfx){ + SensorBase(const String &tn,GwApi *api,const String &prfx):type(tn),prefix(prfx){ } using Creator=std::function *(GwApi *api,const String &prfx)>; virtual bool isActive(){return false;}; diff --git a/lib/spitask/GwSpiSensor.h b/lib/spitask/GwSpiSensor.h index 80beabd..3d516a5 100644 --- a/lib/spitask/GwSpiSensor.h +++ b/lib/spitask/GwSpiSensor.h @@ -125,7 +125,7 @@ class SSISensor : public SensorBase{ } public: - SSISensor(GwApi *api,const String &prfx, int host):SensorBase(api,prfx) + SSISensor(const String &type,GwApi *api,const String &prfx, int host):SensorBase(type,api,prfx) { busId=host; } From 3513175b5d1280ba74a99ce25c02fd94349521c9 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 21 Mar 2024 14:52:44 +0100 Subject: [PATCH 23/35] generate the grove #defines to easily handle multiple groves --- extra_script.py | 40 +++++- lib/hardware/GwHardware.h | 270 ++------------------------------------ lib/hardware/GwM5Base.h | 71 ++++++++++ lib/hardware/GwM5Grove.h | 31 +++++ lib/hardware/GwM5Grove.in | 145 ++++++++++++++++++++ 5 files changed, 297 insertions(+), 260 deletions(-) create mode 100644 lib/hardware/GwM5Base.h create mode 100644 lib/hardware/GwM5Grove.h create mode 100644 lib/hardware/GwM5Grove.in diff --git a/extra_script.py b/extra_script.py index 51c484a..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() @@ -263,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=[] @@ -423,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/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 5188212..8306409 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -33,16 +33,16 @@ #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 -#ifndef GROOVE_IIC - #define GROOVE_IIC(...) -#endif + //general definitions for M5AtomLite //hint for groove pins: //according to some schematics the numbering is 1,2,3(VCC),4(GND) @@ -162,265 +162,14 @@ #define GWBUTTON_PULLUPDOWN #endif -//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 +#include "GwM5Base.h" +#include "GwM5Grove.h" -//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 - -//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 - GWRESOURCE_USE(GROOVE,SERIAL_GROOVE_485) - #define _GWI_SERIAL_GROOVE GWSERIAL_TYPE_UNI -#endif -#ifdef SERIAL_GROOVE_485_A - GWRESOURCE_USE(GROOVEA,SERIAL_GROOVE_485_A) - #define _GWI_SERIAL_GROOVE_A GWSERIAL_TYPE_UNI -#endif -#ifdef SERIAL_GROOVE_485_B - GWRESOURCE_USE(GROOVEB,SERIAL_GROOVE_485_B) - #define _GWI_SERIAL_GROOVE_B GWSERIAL_TYPE_UNI -#endif -#ifdef SERIAL_GROOVE_485_C - GWRESOURCE_USE(GROOVEC,SERIAL_GROOVE_485_C) - #define _GWI_SERIAL_GROOVE_C GWSERIAL_TYPE_UNI -#endif -#ifdef SERIAL_GROOVE_232 - GWRESOURCE_USE(GROOVE,SERIAL_GROOVE_232) - #define _GWI_SERIAL_GROOVE GWSERIAL_TYPE_BI -#endif -#ifdef SERIAL_GROOVE_232_A - GWRESOURCE_USE(GROOVEA,SERIAL_GROOVE_232_A) - #define _GWI_SERIAL_GROOVE_A GWSERIAL_TYPE_BI -#endif -#ifdef SERIAL_GROOVE_232_B - GWRESOURCE_USE(GROOVEB,SERIAL_GROOVE_232_B) - #define _GWI_SERIAL_GROOVE_B GWSERIAL_TYPE_BI -#endif -#ifdef SERIAL_GROOVE_232_C - GWRESOURCE_USE(GROOVEC,SERIAL_GROOVE_232_C) - #define _GWI_SERIAL_GROOVE_C GWSERIAL_TYPE_BI -#endif - - - - -//http://docs.m5stack.com/en/unit/gps -#ifdef M5_GPS_UNIT - GWRESOURCE_USE(GROOVE,M5_GPS_UNIT) - #define _GWI_SERIAL_GROOVE GWSERIAL_TYPE_RX,"9600" -#endif -#ifdef M5_GPS_UNIT_A - GWRESOURCE_USE(GROOVEA,M5_GPS_UNIT_A) - #define _GWI_SERIAL_GROOVE_A GWSERIAL_TYPE_RX,"9600" -#endif -#ifdef M5_GPS_UNIT_B - GWRESOURCE_USE(GROOVEB,M5_GPS_UNIT_B) - #define _GWI_SERIAL_GROOVE_B GWSERIAL_TYPE_RX,"9600" -#endif -#ifdef M5_GPS_UNIT_C - GWRESOURCE_USE(GROOVEC,M5_GPS_UNIT) - #define _GWI_SERIAL_GROOVE_C GWSERIAL_TYPE_RX,"9600" -#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 -//CAN via groove -#ifdef M5_CANUNIT - GWRESOURCE_USE(GROOVE,M5_CANUNIT) - GWRESOURCE_USE(CAN,M5_CANUNIT) - #define ESP32_CAN_TX_PIN GROOVE_PIN_2 - #define ESP32_CAN_RX_PIN GROOVE_PIN_1 -#endif - -#ifdef M5_CANUNIT_A - GWRESOURCE_USE(GROOVEA,M5_CANUNIT_A) - GWRESOURCE_USE(CAN,M5_CANUNIT_A) - #define ESP32_CAN_TX_PIN GROOVEA_PIN_2 - #define ESP32_CAN_RX_PIN GROOVEA_PIN_1 -#endif -#ifdef M5_CANUNIT_B - GWRESOURCE_USE(GROOVEB,M5_CANUNIT_B) - GWRESOURCE_USE(CAN,M5_CANUNIT_B) - #define ESP32_CAN_TX_PIN GROOVEB_PIN_2 - #define ESP32_CAN_RX_PIN GROOVEA_PIN_1 -#endif -#ifdef M5_CANUNIT_C - GWRESOURCE_USE(GROOVEC,M5_CANUNIT_C) - GWRESOURCE_USE(CAN,M5_CANUNIT_C) - #define ESP32_CAN_TX_PIN GROOVEC_PIN_2 - #define ESP32_CAN_RX_PIN GROOVEC_PIN_1 -#endif - - -#ifdef M5_ENV3 - #ifndef M5_GROOVEIIC - #define M5_GROOVEIIC - #endif - GROOVE_IIC(SHT3X,Z,1) - GROOVE_IIC(QMP6988,Z,1) - #define _GWSHT3X - #define _GWQMP6988 -#endif -#ifdef M5_ENV3_A - #ifndef M5_GROOVEIIC_A - #define M5_GROOVEIIC_A - #endif - GROOVE_IIC(SHT3X,A,1) - GROOVE_IIC(QMP6988,A,1) - #define _GWSHT3X - #define _GWQMP6988 -#endif -#ifdef M5_ENV3_B - #ifndef M5_GROOVEIIC_B - #define M5_GROOVEIIC_B - #endif - GROOVE_IIC(SHT3X,B,1) - GROOVE_IIC(QMP6988,B,1) - #define _GWSHT3X - #define _GWQMP6988 -#endif -#ifdef M5_ENV3_C - #ifndef M5_GROOVEIIC_C - #define M5_GROOVEIIC_C - #endif - GROOVE_IIC(SHT3X,C,1) - GROOVE_IIC(QMP6988,C,1) - #define _GWSHT3X - #define _GWQMP6988 -#endif - -#ifdef M5_GROOVEIIC - GWRESOURCE_USE(GROOVE,M5_GROOVEIIC) - #ifndef _GWI_IIC1 - #define _GWI_IIC1 "Z",GROOVE_PIN_1,GROOVE_PIN_2 - #elif ! defined(_GWI_IIC2) - #define _GWI_IIC2 "Z",GROOVE_PIN_1,GROOVE_PIN_2 - #else - #error "both iic buses already in use" - #endif -#endif -#ifdef M5_GROOVEIIC_A - GWRESOURCE_USE(GROOVA,M5_GROOVEIIC_A) - #ifndef _GWI_IIC1 - #define _GWI_IIC1 A,GROOVEA_PIN_1,GROOVEA_PIN_2 - #elif ! defined(_GWI_IIC2) - #define _GWI_IIC2 A,GROOVEA_PIN_1,GROOVEA_PIN_2 - #else - #error "both iic buses already in use" - #endif -#endif -#ifdef M5_GROOVEIIC_B - GWRESOURCE_USE(GROOVB,M5_GROOVEIIC_B) - #ifndef _GWI_IIC1 - #define _GWI_IIC1 B,GROOVEB_PIN_1,GROOVEB_PIN_2 - #elif ! defined(_GWI_IIC2) - #define _GWI_IIC2 B,GROOVEB_PIN_1,GROOVEB_PIN_2 - #else - #error "both iic buses already in use" - #endif -#endif -#ifdef M5_GROOVEIIC_C - GWRESOURCE_USE(GROOVC,M5_GROOVEIIC_C) - #ifndef _GWI_IIC1 - #define _GWI_IIC1 C,GROOVEC_PIN_1,GROOVEC_PIN_2 - #elif ! defined(_GWI_IIC2) - #define _GWI_IIC2 C,GROOVEC_PIN_1,GROOVEC_PIN_2 - #else - #error "both iic buses already in use" - #endif -#endif -#if defined(_GWI_IIC1) || defined (_GWI_IIC2) - #define _GWIIC -#endif - -#ifdef _GWI_SERIAL_GROOVE - #ifndef _GWI_SERIAL1 - #define _GWI_SERIAL1 GROOVE_PIN_1,GROOVE_PIN_2,_GWI_SERIAL_GROOVE - #elif ! defined(_GWI_SERIAL2) - #define _GWI_SERIAL2 GROOVE_PIN_1,GROOVE_PIN_2,_GWI_SERIAL_GROOVE - #else - #error "both serial devices already in use" - #endif -#endif -#ifdef _GWI_SERIAL_GROOVE_A - #ifndef _GWI_SERIAL1 - #define _GWI_SERIAL1 GROOVEA_PIN_1,GROOVEA_PIN_2,_GWI_SERIAL_GROOVE_A - #elif ! defined(_GWI_SERIAL2) - #define _GWI_SERIAL2 GROOVEA_PIN_1,GROOVEA_PIN_2,_GWI_SERIAL_GROOVE_A - #else - #error "both serial devices already in use" - #endif -#endif -#ifdef _GWI_SERIAL_GROOVE_B - #ifndef _GWI_SERIAL1 - #define _GWI_SERIAL1 GROOVEB_PIN_1,GROOVEB_PIN_2,_GWI_SERIAL_GROOVE_B - #elif ! defined(_GWI_SERIAL2) - #define _GWI_SERIAL2 GROOVEB_PIN_1,GROOVEB_PIN_2,_GWI_SERIAL_GROOVE_B - #else - #error "both serial devices already in use" - #endif -#endif -#ifdef _GWI_SERIAL_GROOVE_C - #ifndef _GWI_SERIAL1 - #define _GWI_SERIAL1 GROOVEC_PIN_1,GROOVEC_PIN_2,_GWI_SERIAL_GROOVE_C - #elif ! defined(_GWI_SERIAL2) - #define _GWI_SERIAL2 GROOVEC_PIN_1,GROOVEC_PIN_2,_GWI_SERIAL_GROOVE_C - #else - #error "both serial devices already in use" - #endif -#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 @@ -432,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 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..69e34f8 --- /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 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 SHT3XG1$GS$ + #ifndef M5_GROOVEIIC$GS$ + #define M5_GROOVEIIC$GS$ + #endif + GROOVE_IIC(SHT3X,$Z$,1) + #define _GWSHT3X +#endif + +#GROVE +#ifdef SHT3XG2$GS$ + #ifndef M5_GROOVEIIC$GS$ + #define M5_GROOVEIIC$GS$ + #endif + GROOVE_IIC(SHT3X,$Z$,2) + #define _GWSHT3X +#endif + +#GROVE +#ifdef QMP6988G1$GS$ + #ifndef M5_GROOVEIIC$GS$ + #define M5_GROOVEIIC$GS$ + #endif + GROOVE_IIC(QMP6988,$Z$,1) + #define _GWQMP6988 +#endif + +#GROVE +#ifdef QMP6988G2$GS$ + #ifndef M5_GROOVEIIC$GS$ + #define M5_GROOVEIIC$GS$ + #endif + GROOVE_IIC(QMP6988,$Z$,2) + #define _GWQMP6988 +#endif + +#GROVE +#ifdef BME280G1$GS$ + #ifndef M5_GROOVEIIC$GS$ + #define M5_GROOVEIIC$GS$ + #endif + GROOVE_IIC(BME280,$Z$,1) + #define _GWBME280 +#endif + +#GROVE +#ifdef BME280G2$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 From bef6da33310203c2b4e72184def82d36ddb5d382 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 21 Mar 2024 15:22:36 +0100 Subject: [PATCH 24/35] correctly handle own chipid --- web/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/index.html b/web/index.html index cc17706..6d14fbe 100644 --- a/web/index.html +++ b/web/index.html @@ -102,7 +102,7 @@
chip type - --- + ---
currentVersion From 91062f24ab05f5d58c5fde18d1560528f8a5bc24 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 21 Mar 2024 15:36:14 +0100 Subject: [PATCH 25/35] correctly handle fixed baud rate for grove gps module --- lib/hardware/GwM5Grove.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/hardware/GwM5Grove.in b/lib/hardware/GwM5Grove.in index 69e34f8..35e3a74 100644 --- a/lib/hardware/GwM5Grove.in +++ b/lib/hardware/GwM5Grove.in @@ -18,7 +18,7 @@ This file contains M5 grove definitions. They will be expanded to match the supported groves - Each definition must start with a line start starts with #GROVE + 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) @@ -40,7 +40,7 @@ //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" + #define _GWI_SERIAL_GROOVE$GS$ GWSERIAL_TYPE_RX,9600 #endif #GROVE From bfc6af1f89bf7a0a931e9b82734fe2f201d50a76 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 21 Mar 2024 15:36:35 +0100 Subject: [PATCH 26/35] support portABC base in build config --- webinstall/build.yaml | 54 ++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/webinstall/build.yaml b/webinstall/build.yaml index d583efc..dc4e488 100644 --- a/webinstall/build.yaml +++ b/webinstall/build.yaml @@ -38,57 +38,57 @@ types: - &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 - &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' @@ -101,7 +101,7 @@ types: children: - *m5grooveserial - &gpiopin - type: "#gpiotype#" + type: dropdown resource: "gpio:" help: 'Select the number of the GPIO pin for this function' values: "#gpiopinv#" @@ -136,7 +136,7 @@ types: - 38 - &gpioinput - type: "#gpiotype#" + type: dropdown resource: "gpio:" help: 'Select the number of the GPIO pin for this function' values: "#gpiopinv#" @@ -183,16 +183,6 @@ types: - PPIN25 - PPIN33 - - &abcgpio - - {label:unset, value:} - - ABC_PAYELLOW - - ABC_PAYWHITE - - ABC_PBYELLOW - - ABC_PBYWHITE - - ABC_PBYELLOW - - ABC_PBYWHITE - - - &serialRX <<: *gpioinput key: RX @@ -518,7 +508,18 @@ types: base: busname: "2" bus: "2" - + - &m5pabcchildren + - <<: *m5groove + base: + grv: _A + - <<: *m5groove + base: + grv: _B + - <<: *m5groove + base: + grv: _C + + - &m5base type: select @@ -559,7 +560,8 @@ types: description: "M5 Stack Port ABC extension base" url: "https://docs.m5stack.com/en/unit/AtomPortABC" label: "ABC Ext" - base: + children: + *m5pabcchildren resources: default: &esp32default @@ -577,7 +579,7 @@ config: base: gpiopinv: *gpiopinv gpioinputv: *gpioinputv - gpiotype: dropdown + grv: "" values: - value: m5stack-atom-generic label: m5stack-atom From efd23b0e39805e96a778bd6edc2c4e5491d65f47 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 21 Mar 2024 16:54:37 +0100 Subject: [PATCH 27/35] #37: include the client SSID in the status page --- src/main.cpp | 3 ++- web/index.html | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 672fc7d..599b340 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -410,7 +410,7 @@ public: protected: virtual void processRequest() { - GwJsonDocument status(300 + + GwJsonDocument status(305 + countNMEA2KIn.getJsonSize()+ countNMEA2KOut.getJsonSize() + channels.getJsonSize()+ @@ -418,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; diff --git a/web/index.html b/web/index.html index 6d14fbe..e7d784d 100644 --- a/web/index.html +++ b/web/index.html @@ -37,7 +37,7 @@
wifi client connected - --- + --- [---]
wifi client IP From 4a62e65a29f58ac8a10d34b6c9159bf5c18cb46c Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 21 Mar 2024 17:42:30 +0100 Subject: [PATCH 28/35] make spi devices working again --- lib/spitask/GWDMS22B.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/spitask/GWDMS22B.cpp b/lib/spitask/GWDMS22B.cpp index 91c4964..94734e7 100644 --- a/lib/spitask/GWDMS22B.cpp +++ b/lib/spitask/GWDMS22B.cpp @@ -83,7 +83,7 @@ class GWDMS22B : public SSISensor{ bool invt=false; String zeroConfigName; public: - using SSISensor::SSISensor; + 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); From 8bbe1fd192b2ee20cf77fc52b61fc84ed1142146 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 21 Mar 2024 18:13:37 +0100 Subject: [PATCH 29/35] allow more I2C devices at grove ports --- lib/hardware/GwM5Grove.in | 12 ++++---- webinstall/build.yaml | 63 +++++++++++++++++++++++++++++++++++++++ webinstall/cibuild.js | 24 ++++++++------- 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/lib/hardware/GwM5Grove.in b/lib/hardware/GwM5Grove.in index 35e3a74..6d9ce21 100644 --- a/lib/hardware/GwM5Grove.in +++ b/lib/hardware/GwM5Grove.in @@ -65,7 +65,7 @@ #GROVE //example: -DSHT3XG1_A : defines STH3Xn1 on grove A - x depends on the other devices -#ifdef SHT3XG1$GS$ +#ifdef GWSHT3XG1$GS$ #ifndef M5_GROOVEIIC$GS$ #define M5_GROOVEIIC$GS$ #endif @@ -74,7 +74,7 @@ #endif #GROVE -#ifdef SHT3XG2$GS$ +#ifdef GWSHT3XG2$GS$ #ifndef M5_GROOVEIIC$GS$ #define M5_GROOVEIIC$GS$ #endif @@ -83,7 +83,7 @@ #endif #GROVE -#ifdef QMP6988G1$GS$ +#ifdef GWQMP6988G1$GS$ #ifndef M5_GROOVEIIC$GS$ #define M5_GROOVEIIC$GS$ #endif @@ -92,7 +92,7 @@ #endif #GROVE -#ifdef QMP6988G2$GS$ +#ifdef GWQMP6988G2$GS$ #ifndef M5_GROOVEIIC$GS$ #define M5_GROOVEIIC$GS$ #endif @@ -101,7 +101,7 @@ #endif #GROVE -#ifdef BME280G1$GS$ +#ifdef GWBME280G1$GS$ #ifndef M5_GROOVEIIC$GS$ #define M5_GROOVEIIC$GS$ #endif @@ -110,7 +110,7 @@ #endif #GROVE -#ifdef BME280G2$GS$ +#ifdef GWBME280G2$GS$ #ifndef M5_GROOVEIIC$GS$ #define M5_GROOVEIIC$GS$ #endif diff --git a/webinstall/build.yaml b/webinstall/build.yaml index dc4e488..cdfde68 100644 --- a/webinstall/build.yaml +++ b/webinstall/build.yaml @@ -50,6 +50,69 @@ types: values: - 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#grv# diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index dc87fa6..c20660e 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -652,12 +652,15 @@ class PipelineInfo{ } if (round < 1) continue; if (struct.resource){ - let resList=currentResources[struct.resource]; - if (! resList){ - resList=[]; - currentResources[struct.resource]=resList; - } - resList.push(struct); + let splitted=struct.resource.split(","); + splitted.forEach((resource) => { + let resList = currentResources[resource]; + if (!resList) { + resList = []; + currentResources[resource] = resList; + } + resList.push(struct); + }); } if (target === 'define') { flags += " -D" + struct.value; @@ -680,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); From 41b629e17b4ecdd4c6f8a9cf6a3da54299a84f34 Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 22 Mar 2024 18:02:47 +0100 Subject: [PATCH 30/35] #63: increase iic task stack size to 4000 --- lib/iictask/GwIicTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/iictask/GwIicTask.cpp b/lib/iictask/GwIicTask.cpp index cf88d01..fee671f 100644 --- a/lib/iictask/GwIicTask.cpp +++ b/lib/iictask/GwIicTask.cpp @@ -102,7 +102,7 @@ void initIicTask(GwApi *api){ if ((*it)->preinit(api)) addTask=true; } if (addTask){ - api->addUserTask(runIicTask,"iicTask",3000); + api->addUserTask(runIicTask,"iicTask",4000); } #endif } From f99d6ed2ebd62a8d074debab8c04dea6f6d24911 Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 22 Mar 2024 18:57:43 +0100 Subject: [PATCH 31/35] #60: avoid errors in the web ui when the rx fifo overflows (strange counter ids), add an error log for serial errors --- lib/channel/GwChannel.cpp | 20 +++++++++++++++++--- lib/serial/GwSerial.cpp | 6 +++++- lib/serial/GwSerial.h | 4 ++-- web/index.js | 6 +++++- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/lib/channel/GwChannel.cpp b/lib/channel/GwChannel.cpp index ef9d64e..1b09859 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); } diff --git a/lib/serial/GwSerial.cpp b/lib/serial/GwSerial.cpp index 9327881..640d340 100644 --- a/lib/serial/GwSerial.cpp +++ b/lib/serial/GwSerial.cpp @@ -40,7 +40,7 @@ class GwSerialStream: public Stream{ -GwSerial::GwSerial(GwLog *logger, Stream *s, int id,bool allowRead):serial(s) +GwSerial::GwSerial(GwLog *logger, HardwareSerial *s, int id,bool allowRead):serial(s) { LOG_DEBUG(GwLog::DEBUG,"creating GwSerial %p id %d",this,id); this->id=id; @@ -54,6 +54,10 @@ GwSerial::GwSerial(GwLog *logger, Stream *s, int id,bool allowRead):serial(s) this->readBuffer=new GwBuffer(logger, GwBuffer::RX_BUFFER_SIZE,bufName+"rd"); } buffer->reset("init"); + serial->onReceiveError([this](hardwareSerial_error_t err){ + GwLog *logger=this->logger; + LOG_DEBUG(GwLog::ERROR,"serial error on id %d: %d",this->id,(int)err); + }); initialized=true; } GwSerial::~GwSerial() diff --git a/lib/serial/GwSerial.h b/lib/serial/GwSerial.h index d7b01ed..1cf1920 100644 --- a/lib/serial/GwSerial.h +++ b/lib/serial/GwSerial.h @@ -16,10 +16,10 @@ class GwSerial : public GwChannelInterface{ int id=-1; int overflows=0; size_t enqueue(const uint8_t *data, size_t len,bool partial=false); - Stream *serial; + HardwareSerial *serial; public: static const int bufferSize=200; - GwSerial(GwLog *logger,Stream *stream,int id,bool allowRead=true); + GwSerial(GwLog *logger,HardwareSerial *stream,int id,bool allowRead=true); ~GwSerial(); bool isInitialized(); virtual size_t sendToClients(const char *buf,int sourceId,bool partial=false); diff --git a/web/index.js b/web/index.js index 4c90e44..032b087 100644 --- a/web/index.js +++ b/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); From 5b0b8ba799760e4d244c27bf85e00f6350db698f Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 23 Mar 2024 20:20:50 +0100 Subject: [PATCH 32/35] intermediate: prepare decoder for s3 --- tools/decoder.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/decoder.py b/tools/decoder.py index aa190ae..7f561bc 100755 --- a/tools/decoder.py +++ b/tools/decoder.py @@ -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 From d27e811317ba8d6f569fd977c0d7aaab839d5cd7 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 23 Mar 2024 20:21:21 +0100 Subject: [PATCH 33/35] allow to start m5 atom s3 without USB device connected --- lib/channel/GwChannel.cpp | 2 +- lib/channel/GwChannelList.cpp | 89 +++++++++++++++++++++++++++++------ lib/channel/GwChannelList.h | 10 +++- lib/serial/GwSerial.cpp | 24 ++++++---- lib/serial/GwSerial.h | 8 ++-- 5 files changed, 103 insertions(+), 30 deletions(-) diff --git a/lib/channel/GwChannel.cpp b/lib/channel/GwChannel.cpp index 1b09859..93a2da3 100644 --- a/lib/channel/GwChannel.cpp +++ b/lib/channel/GwChannel.cpp @@ -173,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 bf7817a..fed0332 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -35,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; } @@ -63,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; @@ -127,16 +175,16 @@ static SerialParam *getSerialParam(int id){ } void GwChannelList::addSerial(int id, int rx, int tx, int type){ if (id == 1){ - addSerial(&Serial1,SERIAL1_CHANNEL_ID,type,rx,tx); + addSerial(new SerialWrapper(&Serial1,SERIAL1_CHANNEL_ID),type,rx,tx); return; } if (id == 2){ - addSerial(&Serial2,SERIAL2_CHANNEL_ID,type,rx,tx); + 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(HardwareSerial *stream,int id,int type,int rx,int tx){ +void GwChannelList::addSerial(GwChannelList::SerialWrapperBase *stream,int type,int rx,int tx){ const char *mode=nullptr; switch (type) { @@ -157,9 +205,10 @@ 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); @@ -201,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); @@ -241,6 +290,14 @@ void GwChannelList::preinit(){ } } } +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; @@ -248,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); @@ -297,10 +354,12 @@ void GwChannelList::begin(bool fallbackSerial){ #define GWSERIAL_RX -1 #endif #ifdef GWSERIAL_TYPE - addSerial(&Serial1,SERIAL1_CHANNEL_ID,GWSERIAL_TYPE,GWSERIAL_RX,GWSERIAL_TX); + setSerialError(&Serial1,SERIAL1_CHANNEL_ID,this->logger); + 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); + setSerialError(&Serial1,SERIAL1_CHANNEL_ID,this->logger); + addSerial(new SerialWrapper(&Serial1,SERIAL1_CHANNEL_ID),GWSERIAL_MODE,GWSERIAL_RX,GWSERIAL_TX); #endif #endif //serial 2 @@ -311,10 +370,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 ed11e06..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,8 +36,8 @@ 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); diff --git a/lib/serial/GwSerial.cpp b/lib/serial/GwSerial.cpp index 640d340..c810e58 100644 --- a/lib/serial/GwSerial.cpp +++ b/lib/serial/GwSerial.cpp @@ -40,7 +40,7 @@ class GwSerialStream: public Stream{ -GwSerial::GwSerial(GwLog *logger, HardwareSerial *s, int id,bool allowRead):serial(s) +GwSerial::GwSerial(GwLog *logger, Stream *s, int id,bool allowRead):serial(s) { LOG_DEBUG(GwLog::DEBUG,"creating GwSerial %p id %d",this,id); this->id=id; @@ -54,10 +54,6 @@ GwSerial::GwSerial(GwLog *logger, HardwareSerial *s, int id,bool allowRead):seri this->readBuffer=new GwBuffer(logger, GwBuffer::RX_BUFFER_SIZE,bufName+"rd"); } buffer->reset("init"); - serial->onReceiveError([this](hardwareSerial_error_t err){ - GwLog *logger=this->logger; - LOG_DEBUG(GwLog::ERROR,"serial error on id %d: %d",this->id,(int)err); - }); initialized=true; } GwSerial::~GwSerial() @@ -119,11 +115,21 @@ void GwSerial::readMessages(GwMessageFetcher *writer){ writer->handleBuffer(readBuffer); } -void GwSerial::flush(){ - if (! isInitialized()) return; - while (write() == GwBuffer::AGAIN){ - vTaskDelay(1); +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); diff --git a/lib/serial/GwSerial.h b/lib/serial/GwSerial.h index 1cf1920..5298bc6 100644 --- a/lib/serial/GwSerial.h +++ b/lib/serial/GwSerial.h @@ -16,17 +16,19 @@ class GwSerial : public GwChannelInterface{ int id=-1; int overflows=0; size_t enqueue(const uint8_t *data, size_t len,bool partial=false); - HardwareSerial *serial; + 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,HardwareSerial *stream,int id,bool allowRead=true); + GwSerial(GwLog *logger,Stream *stream,int id,bool allowRead=true); ~GwSerial(); bool isInitialized(); 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 \ No newline at end of file From 4aac6cbc6b452d79828febddd16dcfe8686f3d4d Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 23 Mar 2024 20:29:55 +0100 Subject: [PATCH 34/35] remove old not compiling code for serial defines --- lib/channel/GwChannelList.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp index fed0332..a118f13 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -354,11 +354,9 @@ void GwChannelList::begin(bool fallbackSerial){ #define GWSERIAL_RX -1 #endif #ifdef GWSERIAL_TYPE - setSerialError(&Serial1,SERIAL1_CHANNEL_ID,this->logger); addSerial(new SerialWrapper(&Serial1,SERIAL1_CHANNEL_ID),GWSERIAL_TYPE,GWSERIAL_RX,GWSERIAL_TX); #else #ifdef GWSERIAL_MODE - setSerialError(&Serial1,SERIAL1_CHANNEL_ID,this->logger); addSerial(new SerialWrapper(&Serial1,SERIAL1_CHANNEL_ID),GWSERIAL_MODE,GWSERIAL_RX,GWSERIAL_TX); #endif #endif From 146bc44c6202c5b903835165c691030610ed3915 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 24 Mar 2024 11:23:44 +0100 Subject: [PATCH 35/35] doc for 20240324 --- Readme.md | 18 +++++++++++++++++- doc/Hardware.md | 1 + doc/Sensors.md | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 doc/Sensors.md diff --git a/Readme.md b/Readme.md index da727ef..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,6 +166,21 @@ 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 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]