diff --git a/Readme.md b/Readme.md index db52113..4a05c6d 100644 --- a/Readme.md +++ b/Readme.md @@ -170,6 +170,17 @@ For details refer to the [example description](lib/exampletask/Readme.md). Changelog --------- +[20241128](../../releases/tag/20241128) +********* +* additional correction for: USB connection on S3 stops [#81](../../issues/81) +* [#71](../../pull/71): add BMP280 to [IIC Sensors](doc/Sensors.md), send 130311 for BMP380 and BME380 +* add an api function to add [own Sensors](doc/Sensors.md) +* use a lock on the USB connection write site to avoid problems with NMEA and logs at the same time +* allow to show unmapped XDR values in the data display +* fix a bug that made the dashboard page disappear after a restart of the device +* correctly handle empty fields in RMB messages +* call the newly introduced web request handler for user tasks outside of an API lock + [20241114](../../releases/tag/20241114) ********** * UDP writer and reader - [#79](../../issues/79) diff --git a/doc/Sensors.md b/doc/Sensors.md index faf0ec6..3536874 100644 --- a/doc/Sensors.md +++ b/doc/Sensors.md @@ -1,6 +1,6 @@ Sensors ======= -The software contains support for a couple of sensors (starting from [20231228](../../releases/tag/20231228) and extend in consecutive releases). +The software contains support for a couple of sensors (starting from [20231228](../../releases/tag/20231228) and extended in consecutive releases). This includes some I2C Sensors and SSI rotary encoders. To connect sensors the following steps are necessary: @@ -29,9 +29,57 @@ 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). +Implementing Own Sensors +--------------------- +To add an own sensor implementation you typically need to handle the following parts: +* (opt) add a library that supports your sensor +* add some [XDR Mapping](./XdrMappings.md) that will convert your generated NMEA2000 message into an NMEA0183 XDR record and ensure the display on the data page +* implement the sensor initialization +* implement the measurement and generating the NMEA2000 message + +You typically would do this in a [user task](../lib/exampletask/Readme.md).
+You can either just implement everything by your own or reuse the existing infrastructure for sensors. + +OwnImplementation +__________________ + +To implement everything by your own just create a config.json for the parameters you need, add an XDR mapping in a task init function (see e.g. [PressureXdr](../lib/iictask/GwIicSensors.h#L27)). +In your user taks just initialize the sensor using your config values and add a loop that periodically measures the sensor value and sends out an nmea2000 message (using the [api->sendN2KMessage](../lib/api/GwApi.h#L137)). +To display some information on the status page just add a countergroup in your task init function ([api->addCounter](../lib/api/GwApi.h#L170)) and increment a counter of this group on every measure ([api->increment](../lib/api/GwApi.h#L171)). +To utilize a bus you typically would need to add the related library to your environment and add some bus initialization to define the pins that are used for this particular bus. +Be carefull if you additionally would like to use sensors from the core as the core potentially would already initialize some bus - depending on the compile flags you provide. +If you need additional libraries for your sensor just add a platformio.ini to your usertask and define an environment that contains the additional libraries. +If you would like to compile also other environments (i.e. without the additional libraries) you should wrap all the code that references the additional libraries with some #ifdef and add a define to your environment (see the implementations of the [sensors in the core](../lib/iictask/)). + +Using the core infrastructure +_____________________________ +For sensors of bus types that are already supported by the core (mainly I2C) you can simplify your implementation. +Just also start with a [usertask](../lib/exampletask/Readme.md). But you only need the task init function, a config.json and potentially a platformio.ini. +In your task code just implement a class that handles the sensor - it should inherit from [SensorBase](../lib/sensors/GwSensor.h#L20) or from [IICSensorBase](../lib/iictask/GwIicSensors.h#L16).
+You need to implement: +* _readConfig_ - just read your configuration and fill attributes of your class. Especially set the "ok" to true and fill the interval field to define your measure interval. +* _preinit_ - check if your snesor is configured ,add necessary XDR mappings and return true if your sensor is active. Do __not__ yet initialize the sensor hardware. +* _isActive_ - return true if your sensor is active +* _initDevice_ - init your sensor hardware and return true if this was ok +* _measure_ - read the sensor data, send NMEA2000 messages and increment +counters + +The busType and busId fields of your imnplementation have to be set correctly.
+In your task init function add the sensors you would like to be handled using [api->addSensor](../lib/api/GwApi.h#L218). + +All the internal sensors are implemented using this approach - e.g. [BME280](../lib/iictask/GwBME280.cpp#L23).
+Do not get confused by all the different defines and the special config handling - this is only there to be as generic as possible - typically not necessary for your own sensor implementation. + +To use an IIC bus you need to compile with flags that define the pins to be used for the IIC bus: +* busId 1 (IIC bus 1): GWIIC_SDA, GWIIC_SCL +* busId 2 (IIC bus 2): GWIIC_SDA2, GWIIC_SCL2 + +So you would need to add such definitions to your environment in your platformio.ini. + Implemented Sensors ------------------- -* [BME280](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf): temperature/humidity/pressure [PGNs: 130314,130312, 130313] +* [BME280](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf): temperature/humidity/pressure [PGNs: 130314,130312, 130313, 130311 since 20241128 ] +* [BMP280](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf) [since 20241128]: temperature/pressure [PGNs: 130314,130312, 130311] * [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] diff --git a/lib/api/GwApi.h b/lib/api/GwApi.h index 9d79ef3..88f9690 100644 --- a/lib/api/GwApi.h +++ b/lib/api/GwApi.h @@ -9,6 +9,9 @@ #include "GwSynchronized.h" #include #include +#include "GwSensor.h" +#include +#include class GwApi; typedef void (*GwUserTaskFunction)(GwApi *); //API to be used for additional tasks @@ -43,11 +46,7 @@ class GwApi{ * the core part will not handle this data at all but * this interface ensures that there is a correct locking of the * data to correctly handle multi threading - * The user code should not use this intterface directly - * but instead it should use the static functions - * apiGetXXX(GwApi *,...) - * apiSetXXX(GwApi *,...) - * that will be created by the macro DECLARE_TASK_INTERFACE + * there is no protection - i.e. every task can get and set the data */ class TaskInterfaces { @@ -61,9 +60,9 @@ class GwApi{ }; using Ptr = std::shared_ptr; protected: - virtual bool iset(const String &file, const String &name, Ptr v) = 0; + virtual bool iset(const String &name, Ptr v) = 0; virtual Ptr iget(const String &name, int &result) = 0; - virtual bool iclaim(const String &name, const String &task)=0; + virtual bool iupdate(const String &name,std::function)=0; public: template bool set(const T &v){ @@ -76,6 +75,10 @@ class GwApi{ } template bool claim(const String &task){ + return true; + } + template + bool update(std::function){ return false; } }; @@ -206,7 +209,13 @@ class GwApi{ * @param name: the config name this value is used for * @param value: the current value */ - virtual void setCalibrationValue(const String &name, double value); + virtual void setCalibrationValue(const String &name, double value)=0; + /** + * add a sensor + * depending on the type it will be added to the appropriate task + * @param sensor: created sensor config + */ + virtual void addSensor(SensorBase* sensor,bool readConfig=true){}; /** * not thread safe methods @@ -231,7 +240,10 @@ static void checkDef(T... args){}; #define DECLARE_USERTASK_PARAM(task,...) #endif #ifndef DECLARE_INITFUNCTION -#define DECLARE_INITFUNCTION(task) +#define DECLARE_INITFUNCTION(task,...) +#endif +#ifndef DECLARE_INITFUNCTION_ORDER +#define DECLARE_INITFUNCTION_ORDER(task,...) #endif #ifndef DECLARE_CAPABILITY #define DECLARE_CAPABILITY(name,value) @@ -255,27 +267,20 @@ static void checkDef(T... args){}; * int ival2=99; * String sval="unset"; * }; - * DECLARE_TASKIF(testTask,TestTaskApi); - * The macro will generate 2 static funtions: + * DECLARE_TASKIF(TestTaskApi); * - * bool apiSetTestTaskApi(GwApi *api, const TestTaskApi &v); - * TestTaskApi apiGetTestTaskApi(GwApi *api, int &result); - * - * The setter will return true on success. - * It is intended to be used by the task that did declare the api. - * The getter will set the result to -1 if no data is available, otherwise - * it will return the update count (so you can check if there was a change - * compared to the last call). * It is intended to be used by any task. * Be aware that all the apis share a common namespace - so be sure to somehow * make your API names unique. - * + * To utilize this interface a task can call: + * api->taskInterfaces()->get(res) //and check the result in res + * api->taskInterfaces()->set(value) * */ #define DECLARE_TASKIF_IMPL(type) \ template<> \ inline bool GwApi::TaskInterfaces::set(const type & v) {\ - return iset(__FILE__,#type,GwApi::TaskInterfaces::Ptr(new type(v))); \ + return iset(#type,GwApi::TaskInterfaces::Ptr(new type(v))); \ }\ template<> \ inline type GwApi::TaskInterfaces::get(int &result) {\ @@ -286,13 +291,42 @@ static void checkDef(T... args){}; }\ type *tp=(type*)ptr.get(); \ return type(*tp); \ - }\ + } \ template<> \ - inline bool GwApi::TaskInterfaces::claim(const String &task) {\ - return iclaim(#type,task);\ - }\ + inline bool GwApi::TaskInterfaces::update(std::function f) { \ + return iupdate(#type,[f](GwApi::TaskInterfaces::Ptr cp)->GwApi::TaskInterfaces::Ptr{ \ + if (cp) { \ + f((type *)cp.get()); \ + return cp; \ + } \ + type * et=new type(); \ + bool res=f(et); \ + if (! res){ \ + delete et; \ + return GwApi::TaskInterfaces::Ptr(); \ + } \ + return GwApi::TaskInterfaces::Ptr(et); \ + }); \ + } \ + + #ifndef DECLARE_TASKIF #define DECLARE_TASKIF(type) DECLARE_TASKIF_IMPL(type) #endif + +/** + * do not use this interface directly + * instead use the API function addSensor +*/ +class ConfiguredSensors : public GwApi::TaskInterfaces::Base{ + public: + SensorList sensors; +}; +DECLARE_TASKIF(ConfiguredSensors); + +//order for late init functions +//all user tasks should have lower orders (default: 0) +#define GWLATEORDER 9999 + #endif diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp index 7861f3d..c9db748 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -427,6 +427,7 @@ void GwChannelList::begin(bool fallbackSerial){ if (! fallbackSerial){ GwSerial *usbSerial=createSerialImpl(config, logger,USB_CHANNEL_ID,GWUSB_RX,GWUSB_TX,true); if (usbSerial != nullptr){ + usbSerial->enableWriteLock(); //as it is used for logging we need this additionally GwChannel *usbChannel=createChannel(logger,config,USB_CHANNEL_ID,usbSerial,GWSERIAL_TYPE_BI); if (usbChannel != nullptr){ addChannel(usbChannel); diff --git a/lib/config/GwConverterConfig.h b/lib/config/GwConverterConfig.h index d0dbc6a..4a93a71 100644 --- a/lib/config/GwConverterConfig.h +++ b/lib/config/GwConverterConfig.h @@ -71,9 +71,12 @@ class GwConverterConfig{ int rmcInterval=1000; int rmcCheckTime=4000; int winst312=256; + bool unmappedXdr=false; + unsigned long xdrTimeout=60000; std::vector windMappings; void init(GwConfigHandler *config, GwLog*logger){ minXdrInterval=config->getInt(GwConfigDefinitions::minXdrInterval,100); + xdrTimeout=config->getInt(GwConfigDefinitions::timoSensor); starboardRudderInstance=config->getInt(GwConfigDefinitions::stbRudderI,0); portRudderInstance=config->getInt(GwConfigDefinitions::portRudderI,-1); min2KInterval=config->getInt(GwConfigDefinitions::min2KInterval,50); @@ -83,6 +86,7 @@ class GwConverterConfig{ rmcInterval=config->getInt(GwConfigDefinitions::sendRMCi,1000); if (rmcInterval < 0) rmcInterval=0; if (rmcInterval > 0 && rmcInterval <100) rmcInterval=100; + unmappedXdr=config->getBool(GwConfigDefinitions::unknownXdr); winst312=config->getInt(GwConfigDefinitions::winst312,256); for (auto && it:windConfigs){ String cfg=config->getString(it.second); diff --git a/lib/exampletask/GwExampleTask.cpp b/lib/exampletask/GwExampleTask.cpp index 9b4cbcd..821dd57 100644 --- a/lib/exampletask/GwExampleTask.cpp +++ b/lib/exampletask/GwExampleTask.cpp @@ -156,11 +156,11 @@ class ExampleWebData{ vSemaphoreDelete(lock); } void set(int v){ - GWSYNCHRONIZED(&lock); + GWSYNCHRONIZED(lock); data=v; } int get(){ - GWSYNCHRONIZED(&lock); + GWSYNCHRONIZED(lock); return data; } }; diff --git a/lib/gwwifi/GwWifi.cpp b/lib/gwwifi/GwWifi.cpp index 69715d2..6cd7f72 100644 --- a/lib/gwwifi/GwWifi.cpp +++ b/lib/gwwifi/GwWifi.cpp @@ -108,7 +108,7 @@ void GwWifi::loop(){ } else{ if (! clientIsConnected){ - LOG_DEBUG(GwLog::LOG,"wifiClient %s now connected to",wifiSSID->asCString()); + LOG_DEBUG(GwLog::LOG,"wifiClient now connected to %s at %s",wifiSSID->asCString(),WiFi.localIP().toString().c_str()); clientIsConnected=true; } } diff --git a/lib/hardware/GwM5Grove.in b/lib/hardware/GwM5Grove.in index e0b634f..aed70a1 100644 --- a/lib/hardware/GwM5Grove.in +++ b/lib/hardware/GwM5Grove.in @@ -64,15 +64,15 @@ #endif #GROVE -#ifdef M5_ENV4$GS$ - #ifndef M5_GROOVEIIC$GS$ - #define M5_GROOVEIIC$GS$ - #endif - GROOVE_IIC(SHT3X,$Z$,1) - GROOVE_IIC(BMP280,$Z$,1) - #define _GWSHT3X - #define _GWBMP280 -#endif +//#ifdef M5_ENV4$GS$ +// #ifndef M5_GROOVEIIC$GS$ +// #define M5_GROOVEIIC$GS$ +// #endif +// GROOVE_IIC(SHT3X,$Z$,1) +// GROOVE_IIC(BMP280,$Z$,1) +// #define _GWSHT3X +// #define _GWBMP280 +//#endif #GROVE //example: -DSHT3XG1_A : defines STH3Xn1 on grove A - x depends on the other devices diff --git a/lib/iictask/GwBME280.cpp b/lib/iictask/GwBME280.cpp index 6eb4847..1bf541f 100644 --- a/lib/iictask/GwBME280.cpp +++ b/lib/iictask/GwBME280.cpp @@ -15,11 +15,9 @@ #include #endif #ifdef _GWBME280 -#define TYPE "BME280" -#define PRFX1 TYPE "11" -#define PRFX2 TYPE "12" -#define PRFX3 TYPE "21" -#define PRFX4 TYPE "22" + +class BME280Config; +static GwSensorConfigInitializerList configs; class BME280Config : public IICSensorBase{ public: bool prAct=true; @@ -35,7 +33,7 @@ class BME280Config : public IICSensorBase{ float prOff=0; Adafruit_BME280 *device=nullptr; uint32_t sensorId=-1; - BME280Config(GwApi * api, const String &prfx):SensorBase(TYPE,api,prfx){ + BME280Config(GwApi * api, const String &prfx):IICSensorBase(api,prfx){ } virtual bool isActive(){return prAct||huAct||tmAct;} virtual bool initDevice(GwApi *api,TwoWire *wire){ @@ -57,7 +55,6 @@ class BME280Config : public IICSensorBase{ virtual bool preinit(GwApi * api){ GwLog *logger=api->getLogger(); LOG_DEBUG(GwLog::LOG,"%s configured",prefix.c_str()); - api->addCapability(prefix,"true"); addPressureXdr(api,*this); addTempXdr(api,*this); addHumidXdr(api,*this); @@ -97,96 +94,80 @@ class BME280Config : public IICSensorBase{ sendN2kEnvironmentalParameters(api, *this, temperature, humidity, computed,counterId); } } - #define CFG280(prefix) \ - CFG_GET(prAct,prefix); \ - CFG_GET(tmAct,prefix);\ - CFG_GET(huAct,prefix);\ - CFG_GET(tmSrc,prefix);\ - CFG_GET(huSrc,prefix);\ - CFG_GET(iid,prefix);\ - CFG_GET(intv,prefix);\ - CFG_GET(tmNam,prefix);\ - CFG_GET(huNam,prefix);\ - CFG_GET(prNam,prefix);\ - CFG_GET(tmOff,prefix);\ - CFG_GET(prOff,prefix); + virtual void readConfig(GwConfigHandler *cfg) override { if (ok) return; - if (prefix == PRFX1) - { - busId = 1; - addr = 0x76; - CFG280(BME28011); - ok=true; - } - if (prefix == PRFX2) - { - busId = 1; - addr = 0x77; - CFG280(BME28012); - ok=true; - } - if (prefix == PRFX3) - { - busId = 2; - addr = 0x76; - CFG280(BME28021); - ok=true; - } - if (prefix == PRFX4) - { - busId = 2; - addr = 0x77; - CFG280(BME28022); - ok=true; - } - intv *= 1000; + configs.readConfig(this,cfg); } }; -static IICSensorBase::Creator creator([](GwApi *api, const String &prfx){ + +static SensorBase::Creator creator([](GwApi *api, const String &prfx){ return new BME280Config(api,prfx); }); -IICSensorBase::Creator registerBME280(GwApi *api,IICSensorList &sensors){ - #if defined(GWBME280) || defined(GWBME28011) +SensorBase::Creator registerBME280(GwApi *api){ +#if defined(GWBME280) || defined(GWBME28011) { - auto *cfg=creator(api,PRFX1); - sensors.add(api,cfg); + api->addSensor(creator(api,"BME28011")); CHECK_IIC1(); #pragma message "GWBME28011 defined" } - #endif - #if defined(GWBME28012) +#endif +#if defined(GWBME28012) { - auto *cfg=creator(api,PRFX2); - sensors.add(api,cfg); + api->addSensor(creator(api,"BME28012")); CHECK_IIC1(); #pragma message "GWBME28012 defined" } - #endif - #if defined(GWBME28021) +#endif +#if defined(GWBME28021) { - auto *cfg=creator(api,PRFX3); - sensors.add(api,cfg); + api->addSensor(creator(api,"BME28021")); CHECK_IIC2(); #pragma message "GWBME28021 defined" } - #endif - #if defined(GWBME28022) +#endif +#if defined(GWBME28022) { - auto *cfg=creator(api,PRFX4); - sensors.add(api,cfg); + api->addSensor(creator(api,"BME28022")); CHECK_IIC1(); #pragma message "GWBME28022 defined" } - #endif +#endif return creator; } + +#define CFG280(s, prefix, bus, baddr) \ + CFG_SGET(s, prAct, prefix); \ + CFG_SGET(s, tmAct, prefix); \ + CFG_SGET(s, huAct, prefix); \ + CFG_SGET(s, tmSrc, prefix); \ + CFG_SGET(s, huSrc, prefix); \ + CFG_SGET(s, iid, prefix); \ + CFG_SGET(s, intv, prefix); \ + CFG_SGET(s, tmNam, prefix); \ + CFG_SGET(s, huNam, prefix); \ + CFG_SGET(s, prNam, prefix); \ + CFG_SGET(s, tmOff, prefix); \ + CFG_SGET(s, prOff, prefix); \ + s->busId = bus; \ + s->addr = baddr; \ + s->ok = true; \ + s->intv *= 1000; + +#define SCBME280(list, prefix, bus, addr) \ + GWSENSORCONFIG(list, BME280Config, prefix, [](BME280Config *s, GwConfigHandler *cfg) { CFG280(s, prefix, bus, addr); }); + +SCBME280(configs,BME28011,1,0x76); +SCBME280(configs,BME28012,1,0x77); +SCBME280(configs,BME28021,2,0x76); +SCBME280(configs,BME28022,2,0x77); + #else -IICSensorBase::Creator registerBME280(GwApi *api,IICSensorList &sensors){ - return IICSensorBase::Creator(); +SensorBase::Creator registerBME280(GwApi *api){ + return SensorBase::Creator(); } #endif diff --git a/lib/iictask/GwBME280.h b/lib/iictask/GwBME280.h index 6da8c44..c74adf2 100644 --- a/lib/iictask/GwBME280.h +++ b/lib/iictask/GwBME280.h @@ -1,5 +1,5 @@ #ifndef _GWBME280_H #define _GWBME280_H #include "GwIicSensors.h" -IICSensorBase::Creator registerBME280(GwApi *api,IICSensorList &sensors); +SensorBase::Creator registerBME280(GwApi *api); #endif \ No newline at end of file diff --git a/lib/iictask/GwBMP280.cpp b/lib/iictask/GwBMP280.cpp index c1b1729..e51bd4b 100644 --- a/lib/iictask/GwBMP280.cpp +++ b/lib/iictask/GwBMP280.cpp @@ -15,11 +15,16 @@ #include #endif #ifdef _GWBMP280 -#define TYPE "BMP280" -#define PRFX1 TYPE "11" -#define PRFX2 TYPE "12" -#define PRFX3 TYPE "21" -#define PRFX4 TYPE "22" + + +/** + * we need a forward declaration here as the config list has to go before the + * class implementation + */ + +class BMP280Config; +static GwSensorConfigInitializerList configs; + class BMP280Config : public IICSensorBase{ public: bool prAct=true; @@ -33,14 +38,13 @@ class BMP280Config : public IICSensorBase{ float prOff=0; Adafruit_BMP280 *device=nullptr; uint32_t sensorId=-1; - BMP280Config(GwApi * api, const String &prfx):SensorBase(TYPE,api,prfx){ - } + using IICSensorBase::IICSensorBase; virtual bool isActive(){return prAct||tmAct;} virtual bool initDevice(GwApi *api,TwoWire *wire){ GwLog *logger=api->getLogger(); device= new Adafruit_BMP280(wire); if (! device->begin(addr)){ - LOG_DEBUG(GwLog::ERROR,"unable to initialize %s at %d",prefix.c_str(),addr); + LOG_DEBUG(GwLog::ERROR,"unable to initialize %s at 0x%x",prefix.c_str(),addr); delete device; device=nullptr; return false; @@ -52,7 +56,6 @@ class BMP280Config : public IICSensorBase{ virtual bool preinit(GwApi * api){ GwLog *logger=api->getLogger(); LOG_DEBUG(GwLog::LOG,"%s configured",prefix.c_str()); - api->addCapability(prefix,"true"); addPressureXdr(api,*this); addTempXdr(api,*this); return isActive(); @@ -85,96 +88,90 @@ class BMP280Config : public IICSensorBase{ sendN2kEnvironmentalParameters(api, *this, temperature, humidity, computed,counterId); } } - #define CFGBMP280(prefix) \ - CFG_GET(prAct,prefix); \ - CFG_GET(tmAct,prefix);\ - CFG_GET(tmSrc,prefix);\ - CFG_GET(iid,prefix);\ - CFG_GET(intv,prefix);\ - CFG_GET(tmNam,prefix);\ - CFG_GET(prNam,prefix);\ - CFG_GET(tmOff,prefix);\ - CFG_GET(prOff,prefix); - + virtual void readConfig(GwConfigHandler *cfg) override { - if (prefix == PRFX1) - { - busId = 1; - addr = 0x76; - CFGBMP280(BMP28011); - ok=true; - } - if (prefix == PRFX2) - { - busId = 1; - addr = 0x77; - CFGBMP280(BMP28012); - ok=true; - } - if (prefix == PRFX3) - { - busId = 2; - addr = 0x76; - CFGBMP280(BMP28021); - ok=true; - } - if (prefix == PRFX4) - { - busId = 2; - addr = 0x77; - CFGBMP280(BMP28022); - ok=true; - } - intv *= 1000; + if (ok) return; + configs.readConfig(this,cfg); } }; -static IICSensorBase::Creator creator([](GwApi *api, const String &prfx){ + +static SensorBase::Creator creator([](GwApi *api, const String &prfx)->BMP280Config*{ + if (! configs.knowsPrefix(prfx)){ + return nullptr; + } return new BMP280Config(api,prfx); }); -IICSensorBase::Creator registerBMP280(GwApi *api,IICSensorList &sensors){ +SensorBase::Creator registerBMP280(GwApi *api){ #if defined(GWBMP280) || defined(GWBMP28011) { - auto *cfg=creator(api,PRFX1); - //BMP280Config *cfg=new BMP280Config(api,PRFX1); - sensors.add(api,cfg); + api->addSensor(creator(api,"BMP28011")); CHECK_IIC1(); #pragma message "GWBMP28011 defined" } #endif #if defined(GWBMP28012) { - auto *cfg=creator(api,PRFX2); - //BMP280Config *cfg=new BMP280Config(api,PRFX2); - sensors.add(api,cfg); + api->addSensor(creator(api,"BMP28012")); CHECK_IIC1(); #pragma message "GWBMP28012 defined" } #endif #if defined(GWBMP28021) { - auto *cfg=creator(api,PRFX3); - //BMP280Config *cfg=new BMP280Config(api,PRFX3); - sensors.add(api,cfg); + api->addSensor(creator(api,"BMP28021")); CHECK_IIC2(); #pragma message "GWBMP28021 defined" } #endif #if defined(GWBMP28022) { - auto *cfg=creator(api,PRFX4); - //BMP280Config *cfg=new BMP280Config(api,PRFX4); - sensors.add(api,cfg); + api->addSensor(creator(api,"BMP28022")); CHECK_IIC1(); #pragma message "GWBMP28022 defined" } #endif return creator; } + +/** + * a define for the readConfig function + * we use a define here as we want to be able to check the config + * definitions at compile time +*/ +#define CFGBMP280P(s, prefix, bus, baddr) \ + CFG_SGET(s, prAct, prefix); \ + CFG_SGET(s, tmAct, prefix); \ + CFG_SGET(s, tmSrc, prefix); \ + CFG_SGET(s, iid, prefix); \ + CFG_SGET(s, intv, prefix); \ + CFG_SGET(s, tmNam, prefix); \ + CFG_SGET(s, prNam, prefix); \ + CFG_SGET(s, tmOff, prefix); \ + CFG_SGET(s, prOff, prefix); \ + s->busId = bus; \ + s->addr = baddr; \ + s->ok = true; \ + s->intv*=1000; + +/** + * a config initializer for our sensor +*/ +#define SCBMP280(list, prefix, bus, addr) \ + GWSENSORCONFIG(list, BMP280Config, prefix, [](BMP280Config *s, GwConfigHandler *cfg) { CFGBMP280P(s, prefix, bus, addr); }); + +/** + * four possible sensor configs +*/ +SCBMP280(configs, BMP28011, 1, 0x76); +SCBMP280(configs, BMP28012, 1, 0x77); +SCBMP280(configs, BMP28021, 2, 0x76); +SCBMP280(configs, BMP28022, 2, 0x77); + #else -IICSensorBase::Creator registerBMP280(GwApi *api,IICSensorList &sensors){ - return IICSensorBase::Creator(); +SensorBase::Creator registerBMP280(GwApi *api){ + return SensorBase::Creator(); } #endif diff --git a/lib/iictask/GwBMP280.h b/lib/iictask/GwBMP280.h index 97fd4fe..f5a3e2c 100644 --- a/lib/iictask/GwBMP280.h +++ b/lib/iictask/GwBMP280.h @@ -1,6 +1,6 @@ #ifndef _GWBMP280_H #define _GWBMP280_H #include "GwIicSensors.h" -IICSensorBase::Creator registerBMP280(GwApi *api,IICSensorList &sensors); +SensorBase::Creator registerBMP280(GwApi *api); #endif diff --git a/lib/iictask/GwIicSensors.h b/lib/iictask/GwIicSensors.h index bade5d0..4937daa 100644 --- a/lib/iictask/GwIicSensors.h +++ b/lib/iictask/GwIicSensors.h @@ -11,9 +11,9 @@ class TwoWire; #endif -using BusType=TwoWire; -using IICSensorList=SensorList; -using IICSensorBase=SensorBase; +using BUSTYPE=TwoWire; +using IICSensorList=SensorList; +using IICSensorBase=SensorTemplate; template diff --git a/lib/iictask/GwIicTask.cpp b/lib/iictask/GwIicTask.cpp index 22a7167..221a420 100644 --- a/lib/iictask/GwIicTask.cpp +++ b/lib/iictask/GwIicTask.cpp @@ -43,8 +43,7 @@ static std::vector iicGroveList; void runIicTask(GwApi *api); -static IICSensorList sensors; -static void addGroveItems(std::vector &creators,GwApi *api, IICSensorList &sensors, const String &bus,const String &grove, int, int) +static void addGroveItems(std::vector &creators,GwApi *api, const String &bus,const String &grove, int, int) { GwLog *logger=api->getLogger(); for (auto &&init : iicGroveList) @@ -61,17 +60,18 @@ static void addGroveItems(std::vector &creators,GwApi *a { if (! creator) continue; auto *scfg = creator(api, prfx); + if (scfg == nullptr) continue; scfg->readConfig(api->getConfig()); if (scfg->ok) { LOG_DEBUG(GwLog::LOG, "adding %s from grove config", prfx.c_str()); - sensors.add(api, scfg); + api->addSensor(scfg,false); found=true; break; } else { - LOG_DEBUG(GwLog::DEBUG, "unmatched grove sensor config %s for %s", prfx.c_str(), scfg->type.c_str()); + LOG_DEBUG(GwLog::DEBUG, "unmatched grove sensor config %s", prfx.c_str()); delete scfg; } } @@ -89,19 +89,26 @@ void initIicTask(GwApi *api){ #else bool addTask=false; GwConfigHandler *config=api->getConfig(); - std::vector creators; - creators.push_back(registerSHT3X(api,sensors)); - creators.push_back(registerQMP6988(api,sensors)); - creators.push_back(registerBME280(api,sensors)); - creators.push_back(registerBMP280(api,sensors)); + std::vector creators; + creators.push_back(registerSHT3X(api)); + creators.push_back(registerQMP6988(api)); + creators.push_back(registerBME280(api)); + creators.push_back(registerBMP280(api)); #ifdef _GWI_IIC1 - addGroveItems(creators,api,sensors,"1",_GWI_IIC1); + addGroveItems(creators,api,"1",_GWI_IIC1); #endif #ifdef _GWI_IIC2 - addGroveItems(creators,api,sensors,"2",_GWI_IIC2); + addGroveItems(creators,api,"2",_GWI_IIC2); #endif - for (auto it=sensors.begin();it != sensors.end();it++){ - if ((*it)->preinit(api)) addTask=true; + //TODO: ensure that we run after other init tasks... + int res=-1; + ConfiguredSensors sensorList=api->taskInterfaces()->get(res); + for (auto &&it: sensorList.sensors){ + if (it->busType != SensorBase::IIC) continue; + if (it->preinit(api)) { + addTask=true; + api->addCapability(it->prefix,"true"); + } } if (addTask){ api->addUserTask(runIicTask,"iicTask",4000); @@ -154,8 +161,11 @@ void runIicTask(GwApi *api){ GwLog *logger=api->getLogger(); std::map buses; LOG_DEBUG(GwLog::LOG,"iic task started"); - for (auto it=sensors.begin();it != sensors.end();it++){ - int busId=(*it)->busId; + int res=-1; + ConfiguredSensors sensorList=api->taskInterfaces()->get(res); + for (auto &&it : sensorList.sensors){ + if (it->busType != SensorBase::IIC) continue; + int busId=it->busId; auto bus=buses.find(busId); if (bus == buses.end()){ switch (busId) @@ -175,7 +185,7 @@ void runIicTask(GwApi *api){ } break; default: - LOG_DEBUG(GwLog::ERROR, "invalid bus id %d at config %s", busId, (*it)->prefix.c_str()); + LOG_DEBUG(GwLog::ERROR, "invalid bus id %d at config %s", busId, it->prefix.c_str()); break; } } @@ -184,8 +194,8 @@ void runIicTask(GwApi *api){ bool runLoop=false; GwIntervalRunner timers; int counterId=api->addCounter("iicsensors"); - for (auto it=sensors.begin();it != sensors.end();it++){ - IICSensorBase *cfg=*it; + for (auto && cfg: sensorList.sensors){ + if (cfg->busType != SensorBase::IIC) continue; auto bus=buses.find(cfg->busId); if (! cfg->isActive()) continue; if (bus == buses.end()){ diff --git a/lib/iictask/GwIicTask.h b/lib/iictask/GwIicTask.h index e26eb28..74f935b 100644 --- a/lib/iictask/GwIicTask.h +++ b/lib/iictask/GwIicTask.h @@ -1,6 +1,7 @@ #ifndef _GWIICTASK_H #define _GWIICTASK_H #include "GwApi.h" +#include "GwSensor.h" void initIicTask(GwApi *api); -DECLARE_INITFUNCTION(initIicTask); +DECLARE_INITFUNCTION_ORDER(initIicTask,GWLATEORDER); #endif \ No newline at end of file diff --git a/lib/iictask/GwQMP6988.cpp b/lib/iictask/GwQMP6988.cpp index f3a7671..4f2b78c 100644 --- a/lib/iictask/GwQMP6988.cpp +++ b/lib/iictask/GwQMP6988.cpp @@ -1,11 +1,10 @@ #define _IIC_GROOVE_LIST #include "GwQMP6988.h" #ifdef _GWQMP6988 -#define TYPE "QMP6988" -#define PRFX1 TYPE "11" -#define PRFX2 TYPE "12" -#define PRFX3 TYPE "21" -#define PRFX4 TYPE "22" + +class QMP6988Config; +static GwSensorConfigInitializerList configs; + class QMP6988Config : public IICSensorBase{ public: String prNam="Pressure"; @@ -13,7 +12,7 @@ class QMP6988Config : public IICSensorBase{ tN2kPressureSource prSrc=tN2kPressureSource::N2kps_Atmospheric; float prOff=0; QMP6988 *device=nullptr; - QMP6988Config(GwApi* api,const String &prefix):SensorBase(TYPE,api,prefix){} + QMP6988Config(GwApi* api,const String &prefix):IICSensorBase(api,prefix){} virtual bool isActive(){return prAct;}; virtual bool initDevice(GwApi *api,TwoWire *wire){ if (!isActive()) return false; @@ -31,7 +30,6 @@ class QMP6988Config : public IICSensorBase{ virtual bool preinit(GwApi * api){ GwLog *logger=api->getLogger(); LOG_DEBUG(GwLog::LOG,"QMP6988 configured"); - api->addCapability(prefix,"true"); addPressureXdr(api,*this); return isActive(); } @@ -42,76 +40,43 @@ class QMP6988Config : public IICSensorBase{ LOG_DEBUG(GwLog::DEBUG,"%s measure %2.0fPa, computed %2.0fPa",prefix.c_str(), pressure,computed); sendN2kPressure(api,*this,computed,counterId); } - #define CFG6988(prefix)\ - CFG_GET(prNam,prefix); \ - CFG_GET(iid,prefix); \ - CFG_GET(prAct,prefix); \ - CFG_GET(intv,prefix); \ - CFG_GET(prOff,prefix); + virtual void readConfig(GwConfigHandler *cfg){ if (ok) return; - if (prefix == PRFX1){ - busId=1; - addr=86; - CFG6988(QMP698811); - ok=true; - } - if (prefix == PRFX2){ - busId=1; - addr=112; - CFG6988(QMP698812); - ok=true; - } - if (prefix == PRFX3){ - busId=2; - addr=86; - CFG6988(QMP698821); - ok=true; - } - if (prefix == PRFX4){ - busId=2; - addr=112; - CFG6988(QMP698822); - ok=true; - } - intv*=1000; - + configs.readConfig(this,cfg); } }; -static IICSensorBase::Creator creator=[](GwApi *api,const String &prfx){ +static SensorBase::Creator creator=[](GwApi *api,const String &prfx)-> SensorBase*{ + if (! configs.knowsPrefix(prfx)) return nullptr; return new QMP6988Config(api,prfx); }; -IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors){ +SensorBase::Creator registerQMP6988(GwApi *api){ GwLog *logger=api->getLogger(); #if defined(GWQMP6988) || defined(GWQMP698811) { - QMP6988Config *scfg=new QMP6988Config(api,PRFX1); - sensors.add(api,scfg); + api->addSensor(new QMP6988Config(api,"QMP698811")); CHECK_IIC1(); #pragma message "GWQMP698811 defined" } #endif #if defined(GWQMP698812) { - QMP6988Config *scfg=new QMP6988Config(api,PRFX2); - sensors.add(api,scfg); + api->addSensor(new QMP6988Config(api,"QMP698812")); CHECK_IIC1(); #pragma message "GWQMP698812 defined" } #endif #if defined(GWQMP698821) { - QMP6988Config *scfg=new QMP6988Config(api,PRFX3); - sensors.add(api,scfg); + api->addSensor(new QMP6988Config(api,"QMP698821")); CHECK_IIC2(); #pragma message "GWQMP698821 defined" } #endif #if defined(GWQMP698822) { - QMP6988Config *scfg=new QMP6988Config(api,PRFX4); - sensors.add(api,scfg); + api->addSensor(new QMP6988Config(api,"QMP698822")); CHECK_IIC2(); #pragma message "GWQMP698822 defined" } @@ -119,8 +84,28 @@ IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors){ return creator; } +#define CFG6988(s,prefix,bus,baddr)\ + CFG_SGET(s,prNam,prefix); \ + CFG_SGET(s,iid,prefix); \ + CFG_SGET(s,prAct,prefix); \ + CFG_SGET(s,intv,prefix); \ + CFG_SGET(s,prOff,prefix); \ + s->busId = bus; \ + s->addr = baddr; \ + s->ok = true; \ + s->intv*=1000; + + +#define SC6988(prefix,bus,addr) \ + GWSENSORDEF(configs,QMP6988Config,CFG6988,prefix,bus,addr) + +SC6988(QMP698811,1,86); +SC6988(QMP698812,1,112); +SC6988(QMP698821,2,86); +SC6988(QMP698822,2,112); + #else - IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors){ - return IICSensorBase::Creator(); + SensorBase::Creator registerQMP6988(GwApi *api){ + return SensorBase::Creator(); } #endif \ No newline at end of file diff --git a/lib/iictask/GwQMP6988.h b/lib/iictask/GwQMP6988.h index cb6a881..135cd81 100644 --- a/lib/iictask/GwQMP6988.h +++ b/lib/iictask/GwQMP6988.h @@ -16,5 +16,5 @@ #ifdef _GWQMP6988 #include "QMP6988.h" #endif -IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors); +SensorBase::Creator registerQMP6988(GwApi *api); #endif \ No newline at end of file diff --git a/lib/iictask/GwSHT3X.cpp b/lib/iictask/GwSHT3X.cpp index 40735e1..c93486f 100644 --- a/lib/iictask/GwSHT3X.cpp +++ b/lib/iictask/GwSHT3X.cpp @@ -1,11 +1,7 @@ #include "GwSHT3X.h" #ifdef _GWSHT3X -#define TYPE "SHT3X" -#define PRFX1 TYPE "11" -#define PRFX2 TYPE "12" -#define PRFX3 TYPE "21" -#define PRFX4 TYPE "22" - +class SHT3XConfig; +static GwSensorConfigInitializerList configs; class SHT3XConfig : public IICSensorBase{ public: String tmNam; @@ -15,8 +11,7 @@ class SHT3XConfig : public IICSensorBase{ tN2kHumiditySource huSrc; tN2kTempSource tmSrc; SHT3X *device=nullptr; - SHT3XConfig(GwApi *api,const String &prefix): - SensorBase(TYPE,api,prefix){} + using IICSensorBase::IICSensorBase; virtual bool isActive(){ return tmAct || huAct; } @@ -31,7 +26,6 @@ class SHT3XConfig : public IICSensorBase{ virtual bool preinit(GwApi * api){ GwLog *logger=api->getLogger(); LOG_DEBUG(GwLog::LOG,"%s configured",prefix.c_str()); - api->addCapability(prefix,"true"); addHumidXdr(api,*this); addTempXdr(api,*this); return isActive(); @@ -62,83 +56,43 @@ class SHT3XConfig : public IICSensorBase{ LOG_DEBUG(GwLog::DEBUG, "unable to query %s: %d",prefix.c_str(), rt); } } - /** - * we do not dynamically compute the config names - * just to get compile time errors if something does not fit - * correctly - */ - #define CFG3X(prefix) \ - CFG_GET(tmNam,prefix); \ - CFG_GET(huNam,prefix); \ - CFG_GET(iid,prefix); \ - CFG_GET(tmAct,prefix); \ - CFG_GET(huAct,prefix); \ - CFG_GET(intv,prefix); \ - CFG_GET(huSrc,prefix); \ - CFG_GET(tmSrc,prefix); - + virtual void readConfig(GwConfigHandler *cfg){ if (ok) return; - if (prefix == PRFX1){ - busId=1; - addr=0x44; - CFG3X(SHT3X11); - ok=true; - } - if (prefix == PRFX2){ - busId=1; - addr=0x45; - CFG3X(SHT3X12); - ok=true; - } - if (prefix == PRFX3){ - busId=2; - addr=0x44; - CFG3X(SHT3X21); - ok=true; - } - if (prefix == PRFX4){ - busId=2; - addr=0x45; - CFG3X(SHT3X22); - ok=true; - } - intv*=1000; + configs.readConfig(this,cfg); + return; } }; -IICSensorBase::Creator creator=[](GwApi *api,const String &prfx){ +SensorBase::Creator creator=[](GwApi *api,const String &prfx)-> SensorBase*{ + if (! configs.knowsPrefix(prfx)) return nullptr; return new SHT3XConfig(api,prfx); }; -IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors){ +SensorBase::Creator registerSHT3X(GwApi *api){ GwLog *logger=api->getLogger(); #if defined(GWSHT3X) || defined (GWSHT3X11) { - auto *scfg=creator(api,PRFX1); - sensors.add(api,scfg); + api->addSensor(creator(api,"SHT3X11")); CHECK_IIC1(); #pragma message "GWSHT3X11 defined" } #endif #if defined(GWSHT3X12) { - auto *scfg=creator(api,PRFX2); - sensors.add(api,scfg); + api->addSensor(creator(api,"SHT3X12")); CHECK_IIC1(); #pragma message "GWSHT3X12 defined" } #endif #if defined(GWSHT3X21) { - auto *scfg=creator(api,PRFX3); - sensors.add(api,scfg); + api->addSensor(creator(api,"SHT3X21")); CHECK_IIC2(); #pragma message "GWSHT3X21 defined" } #endif #if defined(GWSHT3X22) { - auto *scfg=creator(api,PRFX4); - sensors.add(api,scfg); + api->addSensor(creator(api,"SHT3X22")); CHECK_IIC2(); #pragma message "GWSHT3X22 defined" } @@ -146,9 +100,36 @@ IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors){ return creator; }; +/** + * we do not dynamically compute the config names + * just to get compile time errors if something does not fit + * correctly + */ +#define CFGSHT3X(s, prefix, bus, baddr) \ + CFG_SGET(s, tmNam, prefix); \ + CFG_SGET(s, huNam, prefix); \ + CFG_SGET(s, iid, prefix); \ + CFG_SGET(s, tmAct, prefix); \ + CFG_SGET(s, huAct, prefix); \ + CFG_SGET(s, intv, prefix); \ + CFG_SGET(s, huSrc, prefix); \ + CFG_SGET(s, tmSrc, prefix); \ + s->busId = bus; \ + s->addr = baddr; \ + s->ok = true; \ + s->intv *= 1000; + +#define SCSHT3X(prefix, bus, addr) \ + GWSENSORDEF(configs, SHT3XConfig, CFGSHT3X, prefix, bus, addr) + +SCSHT3X(SHT3X11, 1, 0x44); +SCSHT3X(SHT3X12, 1, 0x45); +SCSHT3X(SHT3X21, 2, 0x44); +SCSHT3X(SHT3X22, 2, 0x45); + #else -IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors){ - return IICSensorBase::Creator(); +SensorBase::Creator registerSHT3X(GwApi *api){ + return SensorBase::Creator(); } #endif diff --git a/lib/iictask/GwSHT3X.h b/lib/iictask/GwSHT3X.h index f59d51f..6a5dfcf 100644 --- a/lib/iictask/GwSHT3X.h +++ b/lib/iictask/GwSHT3X.h @@ -16,5 +16,5 @@ #ifdef _GWSHT3X #include "SHT3X.h" #endif -IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors); +SensorBase::Creator registerSHT3X(GwApi *api); #endif \ No newline at end of file diff --git a/lib/iictask/platformio.ini b/lib/iictask/platformio.ini index 609ef5a..c0f10f7 100644 --- a/lib/iictask/platformio.ini +++ b/lib/iictask/platformio.ini @@ -11,16 +11,6 @@ build_flags= -D M5_CAN_KIT ${env.build_flags} -[env:m5stack-atom-env4] -extends = sensors -board = m5stack-atom -lib_deps = - ${env.lib_deps} - ${sensors.lib_deps} -build_flags= - -D M5_ENV4 - -D M5_CAN_KIT - ${env.build_flags} [env:m5stack-atom-bme280] extends = sensors @@ -55,6 +45,7 @@ lib_deps = ${sensors.lib_deps} build_flags= -D GWBMP280G1 + -D GWSHT3X11 -D M5_GROOVEIIC -D M5_CAN_KIT ${env.build_flags} diff --git a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp index bd593d5..cf335df 100644 --- a/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp +++ b/lib/nmea0183ton2k/NMEA0183DataToN2K.cpp @@ -189,6 +189,7 @@ private: if (N2kIsNA(v)) return N2kInt8NA; return v; } + void convertXDR(const SNMEA0183Msg &msg){ XdrMappingList foundMappings; for (int offset=0;offset <= (msg.FieldCount()-4);offset+=4){ @@ -199,7 +200,19 @@ private: String unit=msg.Field(offset+2); String transducerName=msg.Field(offset+3); GwXDRFoundMapping found=xdrMappings->getMapping(transducerName,type,unit); - if (found.empty) continue; + if (found.empty) { + if (config.unmappedXdr){ + const GwXDRType *typeDef=xdrMappings->findType(type,unit); + GwXdrUnknownMapping mapping(transducerName,unit,typeDef,config.xdrTimeout); + value=mapping.valueFromXdr(value); + if (boatData->update(value,msg.sourceId,&mapping)){ + //TODO: potentially update the format + LOG_DEBUG(GwLog::DEBUG+1,"found unmapped XDR %s:%s, value %f", + transducerName.c_str(),mapping.getBoatItemFormat().c_str(),value); + } + } + continue; + } value=found.valueFromXdr(value); if (!boatData->update(value,msg.sourceId,&found)) continue; LOG_DEBUG(GwLog::DEBUG+1,"found mapped XDR %s:%s, value %f", @@ -307,7 +320,7 @@ private: return; } tN2kMsg n2kMsg; - if (boatData->XTE->update(rmb.xte,msg.sourceId)){ + if (updateDouble(boatData->XTE,rmb.xte,msg.sourceId)){ tN2kXTEMode mode=N2kxtem_Autonomous; if (msg.FieldCount() > 13){ const char *modeChar=msg.Field(13); @@ -318,10 +331,10 @@ private: } uint8_t destinationId=getWaypointId(rmb.destID); uint8_t sourceId=getWaypointId(rmb.originID); - if (boatData->DTW->update(rmb.dtw,msg.sourceId) - && boatData->BTW->update(rmb.btw,msg.sourceId) - && boatData->WPLat->update(rmb.latitude,msg.sourceId) - && boatData->WPLon->update(rmb.longitude,msg.sourceId) + if (updateDouble(boatData->DTW,rmb.dtw,msg.sourceId) + && updateDouble(boatData->BTW,rmb.btw,msg.sourceId) + && updateDouble(boatData->WPLat,rmb.latitude,msg.sourceId) + && updateDouble(boatData->WPLon,rmb.longitude,msg.sourceId) ){ SetN2kNavigationInfo(n2kMsg,1,rmb.dtw,N2khr_true, false, diff --git a/lib/queue/GwSynchronized.h b/lib/queue/GwSynchronized.h index 786b5f0..08c36c6 100644 --- a/lib/queue/GwSynchronized.h +++ b/lib/queue/GwSynchronized.h @@ -3,14 +3,26 @@ class GwSynchronized{ private: - SemaphoreHandle_t *locker; + SemaphoreHandle_t locker=nullptr; + void lock(){ + if (locker != nullptr) xSemaphoreTake(locker, portMAX_DELAY); + } public: + /** + * deprecated + * as SemaphoreHandle_t is already a pointer just use this directly + */ GwSynchronized(SemaphoreHandle_t *locker){ + if (locker == nullptr) return; + this->locker=*locker; + lock(); + } + GwSynchronized(SemaphoreHandle_t locker){ this->locker=locker; - if (locker != nullptr) xSemaphoreTake(*locker, portMAX_DELAY); + lock(); } ~GwSynchronized(){ - if (locker != nullptr) xSemaphoreGive(*locker); + if (locker != nullptr) xSemaphoreGive(locker); } }; diff --git a/lib/sensors/GwSensor.cpp b/lib/sensors/GwSensor.cpp new file mode 100644 index 0000000..d0d580b --- /dev/null +++ b/lib/sensors/GwSensor.cpp @@ -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 +*/ +#include "GwSensor.h" +#include "GwApi.h" + +void SensorList::add(SensorBase::Ptr sensor){ + this->push_back(sensor); + } \ No newline at end of file diff --git a/lib/sensors/GwSensor.h b/lib/sensors/GwSensor.h index da9b560..3077360 100644 --- a/lib/sensors/GwSensor.h +++ b/lib/sensors/GwSensor.h @@ -14,42 +14,124 @@ */ #ifndef _GWSENSORS_H #define _GWSENSORS_H -#include "GwApi.h" -#include "GwLog.h" -template +#include "GwConfigItem.h" +#include +#include +#include +class GwApi; +class GwConfigHandler; class SensorBase{ public: + using BusType=enum{ + IIC=0, + SPI=1, + UNKNOWN=-1 + }; + using Ptr=std::shared_ptr; + BusType busType=BusType::UNKNOWN; int busId=0; int iid=99; //N2K instanceId int addr=-1; const String prefix; - const String type; long intv=0; bool ok=false; - virtual void readConfig(GwConfigHandler *cfg)=0; - SensorBase(const String &tn,GwApi *api,const String &prfx):type(tn),prefix(prfx){ + SensorBase(BusType bt,GwApi *api,const String &prfx) + :busType(bt),prefix(prfx){ } - using Creator=std::function *(GwApi *api,const String &prfx)>; + using Creator=std::function; virtual bool isActive(){return false;}; - virtual bool initDevice(GwApi *api,BUS *wire){return false;}; + virtual bool initDevice(GwApi *api,void *bus){return false;}; virtual bool preinit(GwApi * api){return false;} - virtual void measure(GwApi * api,BUS *wire, int counterId){}; + virtual void measure(GwApi * api,void *bus, int counterId){}; virtual ~SensorBase(){} + virtual void readConfig(GwConfigHandler *cfg)=0; + +}; +template +class SensorTemplate : public SensorBase{ + public: + SensorTemplate(GwApi *api,const String &prfx) + :SensorBase(bt,api,prfx){} + virtual bool initDevice(GwApi *api,BUS *bus){return false;}; + virtual bool initDevice(GwApi *api,void *bus){ + if (busType != bt) return false; + return initDevice(api,(BUS*)bus); + } + virtual void measure(GwApi * api,void *bus, int counterId){ + if (busType != bt) return; + measure(api,(BUS*)bus,counterId); + }; + protected: + virtual void measure(GwApi *api,BUS *bus, int counterId)=0; }; -template -class SensorList : public std::vector*>{ + +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; + void add(SensorBase::Ptr sensor); + using std::vector::vector; }; +/** + * helper classes for a simplified sensor configuration + * by creating a list of type GwSensorConfigInitializerList we can populate + * it by static instances of GwSensorConfigInitializer (using GWSENSORCONFIG ) that each has + * a function that populates the sensor config from the config data. + * For using sensors this is not really necessary - but it can simplify the code for a sensor + * if you want to support a couple of instances on different buses. + * By using this approach you still can write functions using the CFG_SGET macros that will check + * your config definitions at compile time. + * +*/ +template +class GwSensorConfig{ + public: + using ReadConfig=std::function; + ReadConfig configReader; + String prefix; + GwSensorConfig(const String &prfx,ReadConfig f):prefix(prfx),configReader(f){ + } + bool readConfig(T* s,GwConfigHandler *cfg){ + if (s == nullptr) return false; + configReader(s,cfg); + return s->ok; + } +}; + +template +class GwSensorConfigInitializer : public GwInitializer>{ + public: + using GwInitializer>::GwInitializer; +}; +template +class GwSensorConfigInitializerList : public GwInitializer>::List{ + public: + using GwInitializer>::List::List; + bool readConfig(T *s,GwConfigHandler *cfg){ + for (auto &&scfg:*this){ + if (scfg.readConfig(s,cfg)) return true; + } + return false; + } + bool knowsPrefix(const String &prefix){ + for (auto &&scfg:*this){ + if (scfg.prefix == prefix) return true; + } + return false; + } +}; + +#define CFG_SGET(s, name, prefix) \ + cfg->getValue(s->name, GwConfigDefinitions::prefix##name) + +#define GWSENSORCONFIG(list,type,prefix,initFunction) \ + GwSensorConfigInitializer __init ## type ## prefix(list,GwSensorConfig(#prefix,initFunction)); +#define GWSENSORDEF(list,type,init,prefix,bus,baddr) \ + GWSENSORCONFIG(list, type, prefix, [](type *s, GwConfigHandler *cfg) { init(s, prefix, bus, baddr); }); + #define CFG_GET(name,prefix) \ cfg->getValue(name, GwConfigDefinitions::prefix ## name) + #endif \ No newline at end of file diff --git a/lib/serial/GwSerial.cpp b/lib/serial/GwSerial.cpp index c0fb06e..b23c52f 100644 --- a/lib/serial/GwSerial.cpp +++ b/lib/serial/GwSerial.cpp @@ -63,6 +63,7 @@ GwSerial::~GwSerial() { delete buffer; if (readBuffer) delete readBuffer; + if (lock != nullptr) vSemaphoreDelete(lock); } String GwSerial::getMode(){ @@ -87,10 +88,14 @@ size_t GwSerial::enqueue(const uint8_t *data, size_t len, bool partial) } GwBuffer::WriteStatus GwSerial::write(){ if (! isInitialized()) return GwBuffer::ERROR; - size_t numWrite=availableForWrite(); - size_t rt=buffer->fetchData(numWrite,[](uint8_t *buffer,size_t len, void *p){ - return ((GwSerial *)p)->stream->write(buffer,len); - },this); + size_t rt=0; + { + GWSYNCHRONIZED(lock); + size_t numWrite=availableForWrite(); + rt=buffer->fetchData(numWrite,[](uint8_t *buffer,size_t len, void *p){ + return ((GwSerial *)p)->stream->write(buffer,len); + },this); + } if (rt != 0){ LOG_DEBUG(GwLog::DEBUG+1,"Serial %d write %d",id,rt); } diff --git a/lib/serial/GwSerial.h b/lib/serial/GwSerial.h index f3712ab..1932878 100644 --- a/lib/serial/GwSerial.h +++ b/lib/serial/GwSerial.h @@ -4,6 +4,7 @@ #include "GwLog.h" #include "GwBuffer.h" #include "GwChannelInterface.h" +#include "GwSynchronized.h" #if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 #include "hal/usb_serial_jtag_ll.h" #endif @@ -26,8 +27,12 @@ class GwSerial : public GwChannelInterface{ virtual long getFlushTimeout(){return 2000;} virtual int availableForWrite()=0; int type=0; + SemaphoreHandle_t lock=nullptr; public: GwSerial(GwLog *logger,Stream *stream,int id,int type,bool allowRead=true); + void enableWriteLock(){ + lock=xSemaphoreCreateMutex(); + } virtual ~GwSerial(); bool isInitialized(); virtual size_t sendToClients(const char *buf,int sourceId,bool partial=false); @@ -94,6 +99,7 @@ template if (c->isConnected()){ //this retriggers the ISR usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + usb_serial_jtag_ll_txfifo_flush(); } } return rt; diff --git a/lib/socketserver/GwTcpClient.cpp b/lib/socketserver/GwTcpClient.cpp index e5f914b..2792300 100644 --- a/lib/socketserver/GwTcpClient.cpp +++ b/lib/socketserver/GwTcpClient.cpp @@ -246,7 +246,7 @@ void GwTcpClient::resolveHost(String host) { LOG_DEBUG(GwLog::LOG,"start resolving %s",host.c_str()); { - GWSYNCHRONIZED(&locker); + GWSYNCHRONIZED(locker); resolvedAddress.resolved = false; } state = C_RESOLVING; @@ -283,12 +283,12 @@ void GwTcpClient::resolveHost(String host) void GwTcpClient::setResolved(IPAddress addr, bool valid){ LOG_DEBUG(GwLog::LOG,"setResolved %s, valid=%s", addr.toString().c_str(),(valid?"true":"false")); - GWSYNCHRONIZED(&locker); + GWSYNCHRONIZED(locker); resolvedAddress.address=addr; resolvedAddress.resolved=valid; state=C_RESOLVED; } GwTcpClient::ResolvedAddress GwTcpClient::getResolved(){ - GWSYNCHRONIZED(&locker); + GWSYNCHRONIZED(locker); return resolvedAddress; } \ No newline at end of file diff --git a/lib/spitask/GWDMS22B.cpp b/lib/spitask/GWDMS22B.cpp index 94734e7..fca345e 100644 --- a/lib/spitask/GWDMS22B.cpp +++ b/lib/spitask/GWDMS22B.cpp @@ -21,76 +21,23 @@ #define CHECK_BUS(BUS) \ checkDef("missing config for " #BUS,GW ## BUS ## _CLK ,GW ## BUS ## _MISO); -#define ADD22B(PRFX,BUS) \ - {\ - CHECK_BUS(BUS); \ - GWDMS22B *dms=new GWDMS22B(api,#PRFX,GW ## BUS ## _HOST);\ - sensors.add(api,dms); \ - } - -#ifdef GWDMS22B11 - #define ADD22B11 ADD22B(DMS22B11,SPI1) - #ifndef GWDMS22B11_CS - #define GWDMS22B11_CS -1 - #endif -#else - #define GWDMS22B11_CS -1 - #define ADD22B11 -#endif - -#ifdef GWDMS22B12 - #define ADD22B12 ADD22B(DMS22B12,SPI1) - #ifndef GWDMS22B12_CS - #error "you need to define GWDMS22B12_CS" - #endif - #if GWDMS22B11_CS == -1 - #error "multiple devices on one SPI bus need chip select defines - GWDMS22B11_CS is unset" - #endif -#else - #define GWDMS22B12_CS -1 - #define ADD22B12 -#endif - - -#ifdef GWDMS22B21 - #define ADD22B21 ADD22B(DMS22B21,SPI2) - #ifndef GWDMS22B21_CS - #define GWDMS22B21_CS -1 - #endif -#else - #define GWDMS22B21_CS -1 - #define ADD22B21 -#endif - -#ifdef GWDMS22B22 - #define ADD22B22 ADD22B(DMS22B22,SPI2) - #ifndef GWDMS22B22_CS - #error "you need to define GWDMS22B22_CS" - #endif - #if GWDMS22B21_CS == -1 - #error "multiple devices on one SPI bus need chip select defines - GWDMS22B21_CS is unset" - #endif -#else - #define GWDMS22B22_CS -1 - #define ADD22B22 -#endif - +class GWDMS22B; +static GwSensorConfigInitializerList configs; class GWDMS22B : public SSISensor{ + public: int zero=2047; bool invt=false; String zeroConfigName; - public: - GWDMS22B(GwApi *api,const String &prfx, int host):SSISensor("DMS22B",api,prfx,host){} + GWDMS22B(GwApi *api,const String &prfx):SSISensor(api,prfx){} virtual bool preinit(GwApi * api){ GwLog *logger=api->getLogger(); LOG_DEBUG(GwLog::LOG,"DMS22B configured, prefix=%s, intv=%f, active=%d",prefix.c_str(),fintv,(int)act); - api->addCapability(prefix,"true"); return act; } - virtual void measure(GwApi * api,BusType *bus, int counterId){ + virtual void measure(GwApi * api,BUSTYPE *bus, int counterId){ GwLog *logger=api->getLogger(); uint32_t value=0; esp_err_t res=readData(value); @@ -106,34 +53,84 @@ class GWDMS22B : public SSISensor{ api->increment(counterId,prefix); api->setCalibrationValue(zeroConfigName,(double)value); } - #define DMS22B(PRFX,...) \ - if (prefix == #PRFX) {\ - CFG_GET(act,PRFX); \ - CFG_GET(iid,PRFX); \ - CFG_GET(fintv,PRFX); \ - CFG_GET(zero,PRFX); \ - zeroConfigName=GwConfigDefinitions::PRFX ## zero;\ - CFG_GET(invt,PRFX); \ - bits=12; \ - clock=500000; \ - cs=GW ## PRFX ## _CS; \ - __VA_ARGS__ \ - } - virtual void readConfig(GwConfigHandler *cfg){ - DMS22B(DMS22B11); - DMS22B(DMS22B12); - DMS22B(DMS22B21); - DMS22B(DMS22B22); - intv=1000*fintv; + if (ok) return; + configs.readConfig(this,cfg); } }; -void registerDMS22B(GwApi *api,SpiSensorList &sensors){ - ADD22B11 - ADD22B12 - ADD22B21 - ADD22B22 +#define ADD22B(PRFX,BUS) \ + {\ + CHECK_BUS(BUS); \ + GWDMS22B *dms=new GWDMS22B(api,#PRFX);\ + api->addSensor(dms,true); \ + } +void registerDMS22B(GwApi *api){ +#ifdef GWDMS22B11 + ADD22B(DMS22B11,SPI1) + #ifndef GWDMS22B11_CS + #define GWDMS22B11_CS -1 + #endif +#else + #define GWDMS22B11_CS -1 +#endif + +#ifdef GWDMS22B12 + ADD22B(DMS22B12,SPI1) + #ifndef GWDMS22B12_CS + #error "you need to define GWDMS22B12_CS" + #endif + #if GWDMS22B11_CS == -1 + #error "multiple devices on one SPI bus need chip select defines - GWDMS22B11_CS is unset" + #endif +#else + #define GWDMS22B12_CS -1 +#endif + + +#ifdef GWDMS22B21 + ADD22B(DMS22B21,SPI2) + #ifndef GWDMS22B21_CS + #define GWDMS22B21_CS -1 + #endif +#else + #define GWDMS22B21_CS -1 +#endif + +#ifdef GWDMS22B22 + ADD22B(DMS22B22,SPI2) + #ifndef GWDMS22B22_CS + #error "you need to define GWDMS22B22_CS" + #endif + #if GWDMS22B21_CS == -1 + #error "multiple devices on one SPI bus need chip select defines - GWDMS22B21_CS is unset" + #endif +#else + #define GWDMS22B22_CS -1 +#endif } + + +#define CFGDMS22B(s,PRFX,bus,csv) \ + CFG_SGET(s,act,PRFX); \ + CFG_SGET(s,iid,PRFX); \ + CFG_SGET(s,fintv,PRFX); \ + CFG_SGET(s,zero,PRFX); \ + s->zeroConfigName=GwConfigDefinitions::PRFX ## zero;\ + CFG_SGET(s,invt,PRFX); \ + s->bits=12; \ + s->clock=500000; \ + s->cs=csv; \ + s->busId=bus; \ + s->intv=1000*s->fintv; \ + s->ok=true; + +#define SCDMS22B(prefix,bus) \ + GWSENSORDEF(configs,GWDMS22B,CFGDMS22B,prefix,GW ## bus ## _HOST,GW ## prefix ## _CS) + +SCDMS22B(DMS22B11,SPI1); +SCDMS22B(DMS22B12,SPI1); +SCDMS22B(DMS22B21,SPI2); +SCDMS22B(DMS22B22,SPI2); diff --git a/lib/spitask/GWDMS22B.h b/lib/spitask/GWDMS22B.h index 5ef9bb5..de53804 100644 --- a/lib/spitask/GWDMS22B.h +++ b/lib/spitask/GWDMS22B.h @@ -18,5 +18,5 @@ SSI sensor DMS22B - https://www.mouser.de/datasheet/2/54/bour_s_a0011704065_1-22 #ifndef _GWDMS22B_H #define _GWDMS22B_H #include "GwSpiSensor.h" -void registerDMS22B(GwApi *api,SpiSensorList &sensors); +void registerDMS22B(GwApi *api); #endif \ No newline at end of file diff --git a/lib/spitask/GwSpiSensor.h b/lib/spitask/GwSpiSensor.h index 3d516a5..3491641 100644 --- a/lib/spitask/GwSpiSensor.h +++ b/lib/spitask/GwSpiSensor.h @@ -16,7 +16,7 @@ #ifndef _GWSPISENSOR_H #define _GWSPISENSOR_H #include -#include "GwSensor.h" +#include "GwApi.h" #include class SPIBus{ @@ -48,7 +48,7 @@ class SPIBus{ spi_host_device_t host() const { return hd;} }; -using BusType=SPIBus; +using BUSTYPE=SPIBus; class SSIDevice{ spi_device_handle_t spi; @@ -90,15 +90,16 @@ class SSIDevice{ }; -class SSISensor : public SensorBase{ +class SSISensor : public SensorTemplate{ std::unique_ptr device; - protected: + public: int bits=12; int mask=0xffff; int cs=-1; int clock=0; bool act=false; float fintv=0; + protected: virtual bool initSSI(GwLog*logger,const SPIBus *bus, int clock,int cs, int bits){ mask= (1 << bits)-1; @@ -125,17 +126,16 @@ class SSISensor : public SensorBase{ } public: - SSISensor(const String &type,GwApi *api,const String &prfx, int host):SensorBase(type,api,prfx) + SSISensor(GwApi *api,const String &prfx):SensorTemplate(api,prfx) { - busId=host; } virtual bool isActive(){return act;}; - virtual bool initDevice(GwApi *api,BusType *bus){ + virtual bool initDevice(GwApi *api,BUSTYPE *bus){ return initSSI(api->getLogger(),bus, clock,cs,bits); }; }; -using SpiSensorList=SensorList; +using SpiSensorList=SensorList; #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 e9fff28..a5dda8a 100644 --- a/lib/spitask/GwSpiTask.cpp +++ b/lib/spitask/GwSpiTask.cpp @@ -21,8 +21,6 @@ static SPIBus bus1(GWSPI1_HOST); static SPIBus bus2(GWSPI2_HOST); -static SpiSensorList sensors; - #ifdef GWSPI1_CLK static const int spi1clk=GWSPI1_CLK; #else @@ -57,8 +55,11 @@ static const int spi2mosi=-1; void runSpiTask(GwApi *api){ GwLog *logger=api->getLogger(); + int res=-1; + ConfiguredSensors sensorList=api->taskInterfaces()->get(res); std::map buses; - for (auto && sensor:sensors){ + for (auto && sensor: sensorList.sensors){ + if (sensor->busType != SensorBase::BusType::SPI) continue; int busId=sensor->busId; auto bus=buses.find(busId); if (bus == buses.end()){ @@ -93,7 +94,7 @@ void runSpiTask(GwApi *api){ bool runLoop=false; GwIntervalRunner timers; int counterId=api->addCounter("spisensors"); - for (auto && sensor:sensors){ + for (auto && sensor: sensorList.sensors){ if (!sensor->isActive()) continue; auto bus=buses.find(sensor->busId); if (bus == buses.end()){ @@ -122,10 +123,16 @@ void runSpiTask(GwApi *api){ void initSpiTask(GwApi *api){ GwLog *logger=api->getLogger(); - registerDMS22B(api,sensors); + int res=-1; + registerDMS22B(api); + ConfiguredSensors sensorList=api->taskInterfaces()->get(res); bool addTask=false; - for (auto && sensor:sensors){ - if (sensor->preinit(api)) addTask=true; + for (auto && sensor:sensorList.sensors){ + if (sensor->busType != SensorBase::BusType::SPI) continue; + if (sensor->preinit(api)) { + api->addCapability(sensor->prefix,"true"); + addTask=true; + } } if (addTask){ api->addUserTask(runSpiTask,"spiTask",3000); diff --git a/lib/spitask/GwSpiTask.h b/lib/spitask/GwSpiTask.h index ddcac8b..0714a31 100644 --- a/lib/spitask/GwSpiTask.h +++ b/lib/spitask/GwSpiTask.h @@ -16,5 +16,5 @@ #define _GWSPITASK_H #include "GwApi.h" void initSpiTask(GwApi *api); -DECLARE_INITFUNCTION(initSpiTask); +DECLARE_INITFUNCTION_ORDER(initSpiTask,GWLATEORDER); #endif \ No newline at end of file diff --git a/lib/usercode/GwUserCode.cpp b/lib/usercode/GwUserCode.cpp index bf4502b..1b007f8 100644 --- a/lib/usercode/GwUserCode.cpp +++ b/lib/usercode/GwUserCode.cpp @@ -1,11 +1,12 @@ #define DECLARE_USERTASK(task) GwUserTaskDef __##task##__(task,#task); #define DECLARE_USERTASK_PARAM(task,...) GwUserTaskDef __##task##__(task,#task,__VA_ARGS__); #define DECLARE_INITFUNCTION(task) GwInitTask __Init##task##__(task,#task); +#define DECLARE_INITFUNCTION_ORDER(task,order) GwInitTask __Init##task##__(task,#task,order); #define DECLARE_CAPABILITY(name,value) GwUserCapability __CAP##name##__(#name,#value); #define DECLARE_STRING_CAPABILITY(name,value) GwUserCapability __CAP##name##__(#name,value); #define DECLARE_TASKIF(type) \ DECLARE_TASKIF_IMPL(type) \ - GwIreg __register##type(__FILE__,#type) + static int __taskInterface##type=0; //avoid duplicate declarations #include "GwUserCode.h" #include "GwSynchronized.h" @@ -28,45 +29,6 @@ bool taskExists(V &list, const String &name){ } return false; } -class RegEntry{ - public: - String file; - String task; - RegEntry(const String &t, const String &f):file(f),task(t){} - RegEntry(){} -}; -using RegMap=std::map; -static RegMap ®istrations(){ - static RegMap *regMap=new RegMap(); - return *regMap; -} - -static void registerInterface(const String &task,const String &file, const String &name){ - auto it=registrations().find(name); - if (it != registrations().end()){ - if (it->second.file != file){ - ESP_LOGE("Assert","type %s redefined in %s original in %s",name,file,it->second.file); - std::abort(); - }; - if (it->second.task != task){ - ESP_LOGE("Assert","type %s registered for multiple tasks %s and %s",name,task,it->second.task); - std::abort(); - }; - } - else{ - registrations()[name]=RegEntry(task,file); - } -} - -class GwIreg{ - public: - GwIreg(const String &file, const String &name){ - registerInterface("",file,name); - } - }; - - - class GwUserTaskDef{ public: GwUserTaskDef(TaskFunction_t task,String name, int stackSize=2000){ @@ -82,8 +44,8 @@ class GwInitTask{ GwInitTask(TaskFunction_t task, String name){ initTasks.push_back(GwUserTask(name,task)); } - GwInitTask(GwUserTaskFunction task, String name){ - initTasks.push_back(GwUserTask(name,task)); + GwInitTask(GwUserTaskFunction task, String name,int order=0){ + initTasks.push_back(GwUserTask(name,task,GwUserTask::DEFAULT_STACKSIZE,order)); } }; class GwUserCapability{ @@ -112,21 +74,8 @@ class TaskInterfacesStorage{ logger(l){ lock=xSemaphoreCreateMutex(); } - bool set(const String &file, const String &name, const String &task,GwApi::TaskInterfaces::Ptr v){ - GWSYNCHRONIZED(&lock); - auto it=registrations().find(name); - if (it == registrations().end()){ - LOG_DEBUG(GwLog::ERROR,"TaskInterfaces: invalid set %s not known",name.c_str()); - return false; - } - if (it->second.file != file){ - LOG_DEBUG(GwLog::ERROR,"TaskInterfaces: invalid set %s wrong file, expected %s , got %s",name.c_str(),it->second.file.c_str(),file.c_str()); - return false; - } - if (it->second.task != task){ - LOG_DEBUG(GwLog::ERROR,"TaskInterfaces: invalid set %s wrong task, expected %s , got %s",name.c_str(),it->second.task.c_str(),task.c_str()); - return false; - } + bool set(const String &name, GwApi::TaskInterfaces::Ptr v){ + GWSYNCHRONIZED(lock); auto vit=values.find(name); if (vit != values.end()){ vit->second.updates++; @@ -141,7 +90,7 @@ class TaskInterfacesStorage{ return true; } GwApi::TaskInterfaces::Ptr get(const String &name, int &result){ - GWSYNCHRONIZED(&lock); + GWSYNCHRONIZED(lock); auto it = values.find(name); if (it == values.end()) { @@ -150,36 +99,59 @@ class TaskInterfacesStorage{ } result = it->second.updates; return it->second.ptr; + } + + bool update(const String &name, std::functionf){ + GWSYNCHRONIZED(lock); + auto vit=values.find(name); + bool rt=false; + int mode=0; + if (vit == values.end()){ + mode=1; + auto np=f(GwApi::TaskInterfaces::Ptr()); + if (np){ + mode=11; + values[name]=TaskDataEntry(np); + rt=true; + } + } + else + { + auto np = f(vit->second.ptr); + mode=2; + if (np) + { + mode=22; + vit->second = np; + vit->second.updates++; + if (vit->second.updates < 0) + { + vit->second.updates = 0; + } + rt=true; + } + } + LOG_DEBUG(GwLog::DEBUG,"TaskApi::update %s (mode %d)returns %d",name.c_str(),mode,(int)rt); + return rt; } }; class TaskInterfacesImpl : public GwApi::TaskInterfaces{ - String task; + TaskInterfacesStorage *storage; GwLog *logger; bool isInit=false; public: - TaskInterfacesImpl(const String &n,TaskInterfacesStorage *s, GwLog *l,bool i): - task(n),storage(s),isInit(i),logger(l){} - virtual bool iset(const String &file, const String &name, Ptr v){ - return storage->set(file,name,task,v); + TaskInterfacesImpl(TaskInterfacesStorage *s, GwLog *l,bool i): + storage(s),isInit(i),logger(l){} + protected: + virtual bool iset(const String &name, Ptr v){ + return storage->set(name,v); } virtual Ptr iget(const String &name, int &result){ return storage->get(name,result); } - virtual bool iclaim(const String &name, const String &task){ - if (! isInit) return false; - auto it=registrations().find(name); - if (it == registrations().end()){ - LOG_DEBUG(GwLog::ERROR,"unable to claim interface %s for task %s, not registered",name.c_str(),task.c_str()); - return false; - } - if (!it->second.task.isEmpty()){ - LOG_DEBUG(GwLog::ERROR,"unable to claim interface %s for task %s, already claimed by %s",name.c_str(),task.c_str(),it->second.task.c_str()); - return false; - } - it->second.task=task; - LOG_DEBUG(GwLog::LOG,"claimed interface %s for task %s",name.c_str(),task.c_str()); - return true; + virtual bool iupdate(const String &name,std::function f){ + return storage->update(name,f); } }; @@ -188,7 +160,7 @@ class TaskApi : public GwApiInternal { GwApiInternal *api=nullptr; int sourceId; - SemaphoreHandle_t *mainLock; + SemaphoreHandle_t mainLock; SemaphoreHandle_t localLock; std::map> counter; std::map webHandlers; @@ -200,7 +172,7 @@ class TaskApi : public GwApiInternal public: TaskApi(GwApiInternal *api, int sourceId, - SemaphoreHandle_t *mainLock, + SemaphoreHandle_t mainLock, const String &name, TaskInterfacesStorage *s, bool init=false) @@ -210,7 +182,7 @@ public: this->mainLock=mainLock; this->name=name; localLock=xSemaphoreCreateMutex(); - interfaces=new TaskInterfacesImpl(name,s,api->getLogger(),init); + interfaces=new TaskInterfacesImpl(s,api->getLogger(),init); isInit=init; } virtual GwRequestQueue *getQueue() @@ -264,14 +236,14 @@ public: vSemaphoreDelete(localLock); }; virtual void fillStatus(GwJsonDocument &status){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); if (! counterUsed) return; for (auto it=counter.begin();it != counter.end();it++){ it->second.toJson(status); } }; virtual int getJsonSize(){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); if (! counterUsed) return 0; int rt=0; for (auto it=counter.begin();it != counter.end();it++){ @@ -280,7 +252,7 @@ public: return rt; }; virtual void increment(int idx,const String &name,bool failed=false){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); counterUsed=true; auto it=counter.find(idx); if (it == counter.end()) return; @@ -288,18 +260,18 @@ public: else (it->second.add(name)); }; virtual void reset(int idx){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); counterUsed=true; auto it=counter.find(idx); if (it == counter.end()) return; it->second.reset(); }; virtual void remove(int idx){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); counter.erase(idx); } virtual int addCounter(const String &name){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); counterUsed=true; counterIdx++; //avoid the need for an empty counter constructor @@ -317,7 +289,7 @@ public: return api->addXdrMapping(def); } virtual void registerRequestHandler(const String &url,HandlerFunction handler){ - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); webHandlers[url]=handler; } virtual void addCapability(const String &name, const String &value){ @@ -344,7 +316,7 @@ public: { GwApi::HandlerFunction handler; { - GWSYNCHRONIZED(&localLock); + GWSYNCHRONIZED(localLock); auto it = webHandlers.find(url); if (it == webHandlers.end()) { @@ -357,12 +329,25 @@ public: handler(req); return true; } + virtual void addSensor(SensorBase *sb,bool readConfig=true) override{ + if (sb == nullptr) return; + SensorBase::Ptr sensor(sb); + if (readConfig) sb->readConfig(this->getConfig()); + if (! sensor->ok){ + api->getLogger()->logDebug(GwLog::ERROR,"sensor %s nok , bustype=%d",sensor->prefix.c_str(),(int)sensor->busType); + return; + } + bool rt=taskInterfaces()->update( [sensor,this](ConfiguredSensors *sensors)->bool{ + api->getLogger()->logDebug(GwLog::LOG,"adding sensor %s, type=%d",sensor->prefix,(int)sensor->busType); + sensors->sensors.add(sensor); + return true; + }); + } }; -GwUserCode::GwUserCode(GwApiInternal *api,SemaphoreHandle_t *mainLock){ +GwUserCode::GwUserCode(GwApiInternal *api){ this->logger=api->getLogger(); this->api=api; - this->mainLock=mainLock; this->taskData=new TaskInterfacesStorage(this->logger); } GwUserCode::~GwUserCode(){ @@ -392,6 +377,9 @@ void GwUserCode::startUserTasks(int baseId){ } } void GwUserCode::startInitTasks(int baseId){ + std::sort(initTasks.begin(),initTasks.end(),[](const GwUserTask &a, const GwUserTask &b){ + return a.order < b.order; + }); LOG_DEBUG(GwLog::DEBUG,"starting %d user init tasks",initTasks.size()); for (auto it=initTasks.begin();it != initTasks.end();it++){ LOG_DEBUG(GwLog::LOG,"starting user init task %s with id %d",it->name.c_str(),baseId); diff --git a/lib/usercode/GwUserCode.h b/lib/usercode/GwUserCode.h index 94e745d..af02004 100644 --- a/lib/usercode/GwUserCode.h +++ b/lib/usercode/GwUserCode.h @@ -15,22 +15,25 @@ class GwApiInternal : public GwApi{ }; class GwUserTask{ public: + static const int DEFAULT_STACKSIZE=2000; String name; TaskFunction_t task=NULL; GwUserTaskFunction usertask=NULL; bool isUserTask=false; GwApiInternal *api=NULL; int stackSize=2000; - GwUserTask(String name,TaskFunction_t task,int stackSize=2000){ + int order=0; + GwUserTask(String name,TaskFunction_t task,int stackSize=DEFAULT_STACKSIZE){ this->name=name; this->task=task; this->stackSize=stackSize; } - GwUserTask(String name, GwUserTaskFunction task,int stackSize=2000){ + GwUserTask(String name, GwUserTaskFunction task,int stackSize=DEFAULT_STACKSIZE, int order=0){ this->name=name; this->usertask=task; this->isUserTask=true; this->stackSize=stackSize; + this->order=order; } }; @@ -38,13 +41,14 @@ class TaskInterfacesStorage; class GwUserCode{ GwLog *logger; GwApiInternal *api; - SemaphoreHandle_t *mainLock; + SemaphoreHandle_t mainLock=nullptr; TaskInterfacesStorage *taskData; void startAddOnTask(GwApiInternal *api,GwUserTask *task,int sourceId,String name); public: ~GwUserCode(); typedef std::map Capabilities; - GwUserCode(GwApiInternal *api, SemaphoreHandle_t *mainLock); + GwUserCode(GwApiInternal *api); + void begin(SemaphoreHandle_t mainLock){this->mainLock=mainLock;} void startUserTasks(int baseId); void startInitTasks(int baseId); void startAddonTask(String name,TaskFunction_t task, int id); diff --git a/lib/xdrmappings/GwXDRMappings.cpp b/lib/xdrmappings/GwXDRMappings.cpp index ad26bc8..28f88c2 100644 --- a/lib/xdrmappings/GwXDRMappings.cpp +++ b/lib/xdrmappings/GwXDRMappings.cpp @@ -58,6 +58,7 @@ GwXDRType *types[] = { new GwXDRType(GwXDRType::DISPLACEMENTD, "A", "D",DegToRad,RadToDeg,"rd"), new GwXDRType(GwXDRType::RPM,"T","R") }; +static GwXDRType genericType(GwXDRType::GENERIC, "G", ""); template int GetArrLength(T(&)[size]){return size;} static GwXDRType *findType(GwXDRType::TypeCode type, int *start = NULL) @@ -82,6 +83,19 @@ static GwXDRType *findType(GwXDRType::TypeCode type, int *start = NULL) return NULL; } +static GwXDRType *findType(const String &typeString, const String &unitString) +{ + int len=GetArrLength(types); + for (int i=0; i< len; i++) + { + if (types[i]->xdrtype == typeString && types[i]->xdrunit == unitString) + { + return types[i]; + } + } + return &genericType; +} + #include "GwXdrTypeMappings.h" static GwXDRType::TypeCode findTypeMapping(GwXDRCategory category, int field) @@ -199,7 +213,7 @@ GwXDRMappingDef *GwXDRMappingDef::fromString(String s) } return rt; } -String GwXDRMappingDef::getTransducerName(int instance) +String GwXDRMappingDef::getTransducerName(int instance) const { String name = xdrName; if (instanceMode == GwXDRMappingDef::IS_AUTO) @@ -257,7 +271,7 @@ bool GwXDRMappings::addMapping(GwXDRMappingDef *def) LOG_DEBUG(GwLog::ERROR, "no type mapping for %s", def->toString().c_str()); return false; } - GwXDRType *type = findType(code, &typeIndex); + GwXDRType *type = ::findType(code, &typeIndex); if (!type) { LOG_DEBUG(GwLog::ERROR, "no type definition for %s", def->toString().c_str()); @@ -298,7 +312,7 @@ bool GwXDRMappings::addMapping(GwXDRMappingDef *def) LOG_DEBUG(GwLog::LOG, "append mapping with n183key %s", n183key.c_str()); it->second.push_back(mapping); } - type = findType(code, &typeIndex); + type = ::findType(code, &typeIndex); if (!type) break; mapping = new GwXDRMapping(def, type); @@ -471,7 +485,7 @@ String GwXDRMappings::getXdrEntry(String mapping, double value,int instance){ { return rt; } - GwXDRType *type = findType(code, &typeIndex); + GwXDRType *type = ::findType(code, &typeIndex); bool first=true; unsigned long invalidTime=config->getInt(GwConfigDefinitions::timoSensor); while (type){ @@ -480,8 +494,12 @@ String GwXDRMappings::getXdrEntry(String mapping, double value,int instance){ if (first) first=false; else rt+=","; rt+=found.buildXdrEntry(value).entry; - type = findType(code, &typeIndex); + type = ::findType(code, &typeIndex); } delete def; return rt; } + +const GwXDRType * GwXDRMappings::findType(const String &typeString, const String &unitString) const{ + return ::findType(typeString,unitString); +} \ No newline at end of file diff --git a/lib/xdrmappings/GwXDRMappings.h b/lib/xdrmappings/GwXDRMappings.h index 7643bee..198a729 100644 --- a/lib/xdrmappings/GwXDRMappings.h +++ b/lib/xdrmappings/GwXDRMappings.h @@ -140,7 +140,7 @@ class GwXDRMappingDef{ rt += xdrUnit; return rt; } - String getTransducerName(int instance); + String getTransducerName(int instance) const; private: static bool handleToken(String tok,int index,GwXDRMappingDef *def); }; @@ -163,12 +163,12 @@ class GwXDRFoundMapping : public GwBoatItemNameProvider{ String entry; String transducer; }; - GwXDRMappingDef *definition=NULL; - GwXDRType *type=NULL; + const GwXDRMappingDef *definition=NULL; + const GwXDRType *type=NULL; int instanceId=-1; bool empty=true; unsigned long timeout=0; - GwXDRFoundMapping(GwXDRMappingDef *definition,GwXDRType *type, unsigned long timeout){ + GwXDRFoundMapping(const GwXDRMappingDef *definition,const GwXDRType *type, unsigned long timeout){ this->definition=definition; this->type=type; this->timeout=timeout; @@ -182,7 +182,7 @@ class GwXDRFoundMapping : public GwBoatItemNameProvider{ empty=false; } GwXDRFoundMapping(){} - String getTransducerName(){ + virtual String getTransducerName(){ return definition->getTransducerName(instanceId); } double valueFromXdr(double value){ @@ -203,6 +203,24 @@ class GwXDRFoundMapping : public GwBoatItemNameProvider{ } }; +class GwXdrUnknownMapping : public GwXDRFoundMapping{ + String name; + String unit; + public: + GwXdrUnknownMapping(const String &xdrName, const String &xdrUnit,const GwXDRType *type,unsigned long timeout): + name(xdrName),unit(xdrUnit), GwXDRFoundMapping(nullptr,type,timeout){ + + } + virtual String getTransducerName(){ + return name; + } + virtual String getBoatItemFormat(){ + String rt=GwXDRFoundMapping::getBoatItemFormat(); + if (type->xdrunit.isEmpty()) rt+=unit; + return rt; + } +}; + //the class GwXDRMappings is not intended to be deleted //the deletion will leave memory leaks! class GwConfigHandler; @@ -229,6 +247,7 @@ class GwXDRMappings{ GwXDRFoundMapping getMapping(GwXDRCategory category,int selector,int field=0,int instance=-1); String getXdrEntry(String mapping, double value,int instance=0); const char * getUnMapped(); + const GwXDRType * findType(const String &typeString, const String &unitString) const; }; diff --git a/src/main.cpp b/src/main.cpp index ae8f3d5..c12f906 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -235,17 +235,17 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg, int sourceId,bool conv class CalibrationValues { using Map=std::map; Map values; - SemaphoreHandle_t lock; + SemaphoreHandle_t lock=nullptr; public: CalibrationValues(){ lock=xSemaphoreCreateMutex(); } void set(const String &name,double value){ - GWSYNCHRONIZED(&lock); + GWSYNCHRONIZED(lock); values[name]=value; } bool get(const String &name, double &value){ - GWSYNCHRONIZED(&lock); + GWSYNCHRONIZED(lock); auto it=values.find(name); if (it==values.end()) return false; value=it->second; @@ -373,7 +373,7 @@ bool delayedRestart(){ },"reset",2000,&logger,0,NULL) == pdPASS; } ApiImpl *apiImpl=new ApiImpl(MIN_USER_TASK); -GwUserCode userCodeHandler(apiImpl,&mainLock); +GwUserCode userCodeHandler(apiImpl); #define JSON_OK "{\"status\":\"OK\"}" #define JSON_INVALID_PASS F("{\"status\":\"invalid password\"}") @@ -788,6 +788,7 @@ void setup() { logger.setWriter(new DefaultLogWriter()); #endif boatData.begin(); + userCodeHandler.begin(mainLock); userCodeHandler.startInitTasks(MIN_USER_TASK); channels.preinit(); config.stopChanges(); @@ -849,7 +850,7 @@ void setup() { buffer[29]=0; request->send(200,"text/plain",buffer); }); - webserver.registerHandler((USERPREFIX+"*").c_str(),[&USERPREFIX](AsyncWebServerRequest *req){ + webserver.registerHandler((USERPREFIX+"*").c_str(),[](AsyncWebServerRequest *req){ String turl=req->url().substring(USERPREFIX.length()); logger.logDebug(GwLog::DEBUG,"user web request for %s",turl.c_str()); userCodeHandler.handleWebRequest(turl,req); @@ -937,7 +938,7 @@ void setup() { logger.logDebug(GwLog::LOG,"starting addon tasks"); logger.flush(); { - GWSYNCHRONIZED(&mainLock); + GWSYNCHRONIZED(mainLock); userCodeHandler.startUserTasks(MIN_USER_TASK); } timers.addAction(HEAP_REPORT_TIME,[](){ @@ -967,7 +968,7 @@ void handleSendAndRead(bool handleRead){ void loopRun() { //logger.logDebug(GwLog::DEBUG,"main loop start"); monitor.reset(); - GWSYNCHRONIZED(&mainLock); + GWSYNCHRONIZED(mainLock); logger.flush(); monitor.setTime(1); gwWifi.loop(); diff --git a/web/config.json b/web/config.json index 0b75d27..26d3166 100644 --- a/web/config.json +++ b/web/config.json @@ -210,6 +210,14 @@ "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":"unknownXdr", + "label":"show unknown XDR", + "type":"boolean", + "default":"false", + "description":"show received XDR transducer values in data display if there is no XDR mapping for them", + "category":"converter" + }, { "name":"sendRMCi", "label":"send RMC interval", diff --git a/web/index.js b/web/index.js index 538a51a..bbc96e3 100644 --- a/web/index.js +++ b/web/index.js @@ -1578,7 +1578,7 @@ if (isNaN(x)) return '-----'; return formatLonLatsDecimal(x, 'lat'); }, - u: '°' + u: '' }, formatLongitude: { f: function (v) { @@ -1798,7 +1798,7 @@ let id = el.getAttribute('id'); if (id) { if (!names[id.replace(/^frame_/, '')]) { - el.parentElement.remove(); + el.remove(); } } }); diff --git a/webinstall/build.yaml b/webinstall/build.yaml index 7af2679..0fcca66 100644 --- a/webinstall/build.yaml +++ b/webinstall/build.yaml @@ -40,6 +40,8 @@ types: type: frame key: m5groovei2c#grv# label: "M5 I2C Groove Units" + target: resource + resource: i2cbus children: - label: "M5 ENV3" type: checkbox @@ -51,16 +53,16 @@ types: - value: M5_ENV3#grv# key: true resource: qmp69881#grv#1,sht3x#grv#1 - - label: "M5 ENV4" - type: checkbox - key: m5env4#grv# - target: define - url: "https://docs.m5stack.com/en/unit/ENV%E2%85%A3%20Unit" - description: "M5 sensor module temperature, humidity, pressure" - values: - - value: M5_ENV4#grv# - key: true - resource: bmp280#grv#1,sht3x#grv#1 +# - label: "M5 ENV4" +# type: checkbox +# key: m5env4#grv# +# target: define +# url: "https://docs.m5stack.com/en/unit/ENV%E2%85%A3%20Unit" +# description: "M5 sensor module temperature, humidity, pressure" +# values: +# - value: M5_ENV4#grv# +# key: true +# resource: bmp280#grv#1,sht3x#grv#1 - type: checkbox label: SHT3X-1 description: "SHT30 temperature and humidity sensor 0x44" @@ -682,7 +684,7 @@ resources: default: &esp32default serial: 2 can: 1 - i2c: 1 + i2cbus: 2 gpio: 1 config: diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index c20660e..26443b0 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -686,7 +686,7 @@ class PipelineInfo{ let allowed=allowedResources[ak]; if (allowed === undefined) allowed=1; if (resList.length > allowed){ - errors+=" more than "+allowed+" "+k+" device(s) used"; + errors+=" more than "+allowed+" device(s) of type "+k+" used"; } }