Merge branch 'wellenvogel:master' into master
This commit is contained in:
		
						commit
						4f0fd9721e
					
				
							
								
								
									
										11
									
								
								Readme.md
								
								
								
								
							
							
						
						
									
										11
									
								
								Readme.md
								
								
								
								
							|  | @ -170,6 +170,17 @@ For details refer to the [example description](lib/exampletask/Readme.md). | ||||||
| 
 | 
 | ||||||
| Changelog | 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) | [20241114](../../releases/tag/20241114) | ||||||
| ********** | ********** | ||||||
| * UDP writer and reader - [#79](../../issues/79) | * UDP writer and reader - [#79](../../issues/79) | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| Sensors | 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. | This includes some I2C Sensors and SSI rotary encoders. | ||||||
| To connect sensors the following steps are necessary: | 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). | 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).<br> | ||||||
|  | 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).<br> | ||||||
|  | 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.<br> | ||||||
|  | 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).<br> | ||||||
|  | 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 | 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] | * [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] | * [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] | * [M5-ENV3](https://docs.m5stack.com/en/unit/envIII): combination of QMP6988 and SHT30 [PGNs: 130314,130312, 130313] | ||||||
|  |  | ||||||
|  | @ -9,6 +9,9 @@ | ||||||
| #include "GwSynchronized.h" | #include "GwSynchronized.h" | ||||||
| #include <map> | #include <map> | ||||||
| #include <ESPAsyncWebServer.h> | #include <ESPAsyncWebServer.h> | ||||||
|  | #include "GwSensor.h" | ||||||
|  | #include <functional> | ||||||
|  | #include <memory> | ||||||
| class GwApi; | class GwApi; | ||||||
| typedef void (*GwUserTaskFunction)(GwApi *); | typedef void (*GwUserTaskFunction)(GwApi *); | ||||||
| //API to be used for additional tasks
 | //API to be used for additional tasks
 | ||||||
|  | @ -43,11 +46,7 @@ class GwApi{ | ||||||
|          * the core part will not handle this data at all but |          * the core part will not handle this data at all but | ||||||
|          * this interface ensures that there is a correct locking of the |          * this interface ensures that there is a correct locking of the | ||||||
|          * data to correctly handle multi threading |          * data to correctly handle multi threading | ||||||
|          * The user code should not use this intterface directly |          * there is no protection - i.e. every task can get and set the data | ||||||
|          * but instead it should use the static functions |  | ||||||
|          * apiGetXXX(GwApi *,...) |  | ||||||
|          * apiSetXXX(GwApi *,...) |  | ||||||
|          * that will be created by the macro DECLARE_TASK_INTERFACE |  | ||||||
|          */ |          */ | ||||||
|         class TaskInterfaces |         class TaskInterfaces | ||||||
|         { |         { | ||||||
|  | @ -61,9 +60,9 @@ class GwApi{ | ||||||
|             }; |             }; | ||||||
|             using Ptr = std::shared_ptr<Base>; |             using Ptr = std::shared_ptr<Base>; | ||||||
|         protected: |         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 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<Ptr(Ptr v)>)=0; | ||||||
|         public: |         public: | ||||||
|             template <typename T> |             template <typename T> | ||||||
|             bool set(const T &v){ |             bool set(const T &v){ | ||||||
|  | @ -76,6 +75,10 @@ class GwApi{ | ||||||
|             } |             } | ||||||
|             template <typename T> |             template <typename T> | ||||||
|             bool claim(const String &task){ |             bool claim(const String &task){ | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             template <typename T> | ||||||
|  |             bool update(std::function<bool(T *)>){ | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|  | @ -206,7 +209,13 @@ class GwApi{ | ||||||
|          * @param name: the config name this value is used for |          * @param name: the config name this value is used for | ||||||
|          * @param value: the current value |          * @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 |          * not thread safe methods | ||||||
|  | @ -231,7 +240,10 @@ static void checkDef(T... args){}; | ||||||
| #define DECLARE_USERTASK_PARAM(task,...) | #define DECLARE_USERTASK_PARAM(task,...) | ||||||
| #endif | #endif | ||||||
| #ifndef DECLARE_INITFUNCTION | #ifndef DECLARE_INITFUNCTION | ||||||
| #define DECLARE_INITFUNCTION(task) | #define DECLARE_INITFUNCTION(task,...) | ||||||
|  | #endif | ||||||
|  | #ifndef DECLARE_INITFUNCTION_ORDER | ||||||
|  | #define DECLARE_INITFUNCTION_ORDER(task,...) | ||||||
| #endif | #endif | ||||||
| #ifndef DECLARE_CAPABILITY | #ifndef DECLARE_CAPABILITY | ||||||
| #define DECLARE_CAPABILITY(name,value) | #define DECLARE_CAPABILITY(name,value) | ||||||
|  | @ -255,27 +267,20 @@ static void checkDef(T... args){}; | ||||||
|  *          int ival2=99; |  *          int ival2=99; | ||||||
|  *          String sval="unset"; |  *          String sval="unset"; | ||||||
|  * }; |  * }; | ||||||
|  * DECLARE_TASKIF(testTask,TestTaskApi); |  * DECLARE_TASKIF(TestTaskApi); | ||||||
|  * The macro will generate 2 static funtions: |  | ||||||
|  *  |  *  | ||||||
|  * 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. |  * It is intended to be used by any task. | ||||||
|  * Be aware that all the apis share a common namespace - so be sure to somehow |  * Be aware that all the apis share a common namespace - so be sure to somehow | ||||||
|  * make your API names unique. |  * make your API names unique. | ||||||
|  *  |  * To utilize this interface a task can call: | ||||||
|  |  * api->taskInterfaces()->get<TestTaskApi>(res) //and check the result in res
 | ||||||
|  |  * api->taskInterfaces()->set<TestTaskApi>(value) | ||||||
|  *  |  *  | ||||||
| */ | */ | ||||||
| #define DECLARE_TASKIF_IMPL(type) \ | #define DECLARE_TASKIF_IMPL(type) \ | ||||||
|     template<> \ |     template<> \ | ||||||
|     inline bool GwApi::TaskInterfaces::set(const type & v) {\ |     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<> \ |     template<> \ | ||||||
|     inline type GwApi::TaskInterfaces::get<type>(int &result) {\ |     inline type GwApi::TaskInterfaces::get<type>(int &result) {\ | ||||||
|  | @ -286,13 +291,42 @@ static void checkDef(T... args){}; | ||||||
|         }\ |         }\ | ||||||
|         type *tp=(type*)ptr.get(); \ |         type *tp=(type*)ptr.get(); \ | ||||||
|         return type(*tp); \ |         return type(*tp); \ | ||||||
|     }\ |     } \ | ||||||
|     template<> \ |     template<> \ | ||||||
|     inline bool GwApi::TaskInterfaces::claim<type>(const String &task) {\ |     inline bool GwApi::TaskInterfaces::update(std::function<bool(type *)> f) { \ | ||||||
|         return iclaim(#type,task);\ |         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 | #ifndef DECLARE_TASKIF | ||||||
|     #define DECLARE_TASKIF(type) DECLARE_TASKIF_IMPL(type) |     #define DECLARE_TASKIF(type) DECLARE_TASKIF_IMPL(type) | ||||||
| #endif | #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 | #endif | ||||||
|  |  | ||||||
|  | @ -427,6 +427,7 @@ void GwChannelList::begin(bool fallbackSerial){ | ||||||
|     if (! fallbackSerial){ |     if (! fallbackSerial){ | ||||||
|         GwSerial *usbSerial=createSerialImpl(config, logger,USB_CHANNEL_ID,GWUSB_RX,GWUSB_TX,true); |         GwSerial *usbSerial=createSerialImpl(config, logger,USB_CHANNEL_ID,GWUSB_RX,GWUSB_TX,true); | ||||||
|         if (usbSerial != nullptr){ |         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); |             GwChannel *usbChannel=createChannel(logger,config,USB_CHANNEL_ID,usbSerial,GWSERIAL_TYPE_BI); | ||||||
|             if (usbChannel != nullptr){ |             if (usbChannel != nullptr){ | ||||||
|                 addChannel(usbChannel); |                 addChannel(usbChannel); | ||||||
|  |  | ||||||
|  | @ -71,9 +71,12 @@ class GwConverterConfig{ | ||||||
|       int rmcInterval=1000; |       int rmcInterval=1000; | ||||||
|       int rmcCheckTime=4000; |       int rmcCheckTime=4000; | ||||||
|       int winst312=256; |       int winst312=256; | ||||||
|  |       bool unmappedXdr=false; | ||||||
|  |       unsigned long xdrTimeout=60000; | ||||||
|       std::vector<WindMapping> windMappings; |       std::vector<WindMapping> windMappings; | ||||||
|       void init(GwConfigHandler *config, GwLog*logger){ |       void init(GwConfigHandler *config, GwLog*logger){ | ||||||
|         minXdrInterval=config->getInt(GwConfigDefinitions::minXdrInterval,100); |         minXdrInterval=config->getInt(GwConfigDefinitions::minXdrInterval,100); | ||||||
|  |         xdrTimeout=config->getInt(GwConfigDefinitions::timoSensor); | ||||||
|         starboardRudderInstance=config->getInt(GwConfigDefinitions::stbRudderI,0); |         starboardRudderInstance=config->getInt(GwConfigDefinitions::stbRudderI,0); | ||||||
|         portRudderInstance=config->getInt(GwConfigDefinitions::portRudderI,-1); |         portRudderInstance=config->getInt(GwConfigDefinitions::portRudderI,-1); | ||||||
|         min2KInterval=config->getInt(GwConfigDefinitions::min2KInterval,50); |         min2KInterval=config->getInt(GwConfigDefinitions::min2KInterval,50); | ||||||
|  | @ -83,6 +86,7 @@ class GwConverterConfig{ | ||||||
|         rmcInterval=config->getInt(GwConfigDefinitions::sendRMCi,1000); |         rmcInterval=config->getInt(GwConfigDefinitions::sendRMCi,1000); | ||||||
|         if (rmcInterval < 0) rmcInterval=0; |         if (rmcInterval < 0) rmcInterval=0; | ||||||
|         if (rmcInterval > 0 && rmcInterval <100) rmcInterval=100; |         if (rmcInterval > 0 && rmcInterval <100) rmcInterval=100; | ||||||
|  |         unmappedXdr=config->getBool(GwConfigDefinitions::unknownXdr); | ||||||
|         winst312=config->getInt(GwConfigDefinitions::winst312,256); |         winst312=config->getInt(GwConfigDefinitions::winst312,256); | ||||||
|         for (auto && it:windConfigs){ |         for (auto && it:windConfigs){ | ||||||
|           String cfg=config->getString(it.second); |           String cfg=config->getString(it.second); | ||||||
|  |  | ||||||
|  | @ -156,11 +156,11 @@ class ExampleWebData{ | ||||||
|         vSemaphoreDelete(lock); |         vSemaphoreDelete(lock); | ||||||
|     } |     } | ||||||
|     void set(int v){ |     void set(int v){ | ||||||
|         GWSYNCHRONIZED(&lock); |         GWSYNCHRONIZED(lock); | ||||||
|         data=v; |         data=v; | ||||||
|     } |     } | ||||||
|     int get(){ |     int get(){ | ||||||
|         GWSYNCHRONIZED(&lock); |         GWSYNCHRONIZED(lock); | ||||||
|         return data; |         return data; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -108,7 +108,7 @@ void GwWifi::loop(){ | ||||||
|         } |         } | ||||||
|         else{ |         else{ | ||||||
|             if (! clientIsConnected){ |             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; |                 clientIsConnected=true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -64,15 +64,15 @@ | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #GROVE | #GROVE | ||||||
| #ifdef M5_ENV4$GS$ | //#ifdef M5_ENV4$GS$ | ||||||
|   #ifndef M5_GROOVEIIC$GS$ | //  #ifndef M5_GROOVEIIC$GS$ | ||||||
|     #define M5_GROOVEIIC$GS$ | //    #define M5_GROOVEIIC$GS$ | ||||||
|   #endif | //  #endif | ||||||
|   GROOVE_IIC(SHT3X,$Z$,1) | //  GROOVE_IIC(SHT3X,$Z$,1) | ||||||
|   GROOVE_IIC(BMP280,$Z$,1) | //  GROOVE_IIC(BMP280,$Z$,1) | ||||||
|   #define _GWSHT3X | //  #define _GWSHT3X | ||||||
|   #define _GWBMP280 | //  #define _GWBMP280 | ||||||
| #endif | //#endif | ||||||
| 
 | 
 | ||||||
| #GROVE | #GROVE | ||||||
| //example: -DSHT3XG1_A : defines STH3Xn1 on grove A - x depends on the other devices | //example: -DSHT3XG1_A : defines STH3Xn1 on grove A - x depends on the other devices | ||||||
|  |  | ||||||
|  | @ -15,11 +15,9 @@ | ||||||
|     #include <Adafruit_BME280.h> |     #include <Adafruit_BME280.h> | ||||||
| #endif | #endif | ||||||
| #ifdef _GWBME280 | #ifdef _GWBME280 | ||||||
| #define TYPE "BME280" | 
 | ||||||
| #define PRFX1 TYPE "11" | class BME280Config; | ||||||
| #define PRFX2 TYPE "12" | static GwSensorConfigInitializerList<BME280Config> configs; | ||||||
| #define PRFX3 TYPE "21" |  | ||||||
| #define PRFX4 TYPE "22" |  | ||||||
| class BME280Config : public IICSensorBase{ | class BME280Config : public IICSensorBase{ | ||||||
|     public: |     public: | ||||||
|     bool prAct=true; |     bool prAct=true; | ||||||
|  | @ -35,7 +33,7 @@ class BME280Config : public IICSensorBase{ | ||||||
|     float prOff=0; |     float prOff=0; | ||||||
|     Adafruit_BME280 *device=nullptr; |     Adafruit_BME280 *device=nullptr; | ||||||
|     uint32_t sensorId=-1; |     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 isActive(){return prAct||huAct||tmAct;} | ||||||
|     virtual bool initDevice(GwApi *api,TwoWire *wire){ |     virtual bool initDevice(GwApi *api,TwoWire *wire){ | ||||||
|  | @ -57,7 +55,6 @@ class BME280Config : public IICSensorBase{ | ||||||
|     virtual bool preinit(GwApi * api){ |     virtual bool preinit(GwApi * api){ | ||||||
|         GwLog *logger=api->getLogger(); |         GwLog *logger=api->getLogger(); | ||||||
|         LOG_DEBUG(GwLog::LOG,"%s configured",prefix.c_str()); |         LOG_DEBUG(GwLog::LOG,"%s configured",prefix.c_str()); | ||||||
|         api->addCapability(prefix,"true"); |  | ||||||
|         addPressureXdr(api,*this); |         addPressureXdr(api,*this); | ||||||
|         addTempXdr(api,*this); |         addTempXdr(api,*this); | ||||||
|         addHumidXdr(api,*this); |         addHumidXdr(api,*this); | ||||||
|  | @ -97,96 +94,80 @@ class BME280Config : public IICSensorBase{ | ||||||
|             sendN2kEnvironmentalParameters(api, *this, temperature, humidity, computed,counterId); |             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 |     virtual void readConfig(GwConfigHandler *cfg) override | ||||||
|     { |     { | ||||||
|         if (ok) return; |         if (ok) return; | ||||||
|         if (prefix == PRFX1) |         configs.readConfig(this,cfg);        | ||||||
|         { |  | ||||||
|             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; |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static IICSensorBase::Creator creator([](GwApi *api, const String &prfx){ | 
 | ||||||
|  | static SensorBase::Creator creator([](GwApi *api, const String &prfx){ | ||||||
|     return new BME280Config(api,prfx); |     return new BME280Config(api,prfx); | ||||||
| }); | }); | ||||||
| IICSensorBase::Creator registerBME280(GwApi *api,IICSensorList &sensors){ | SensorBase::Creator registerBME280(GwApi *api){ | ||||||
|     #if defined(GWBME280) || defined(GWBME28011) | #if defined(GWBME280) || defined(GWBME28011) | ||||||
|     { |     { | ||||||
|         auto *cfg=creator(api,PRFX1); |         api->addSensor(creator(api,"BME28011")); | ||||||
|         sensors.add(api,cfg); |  | ||||||
|         CHECK_IIC1(); |         CHECK_IIC1(); | ||||||
|         #pragma message "GWBME28011 defined" |         #pragma message "GWBME28011 defined" | ||||||
|     } |     } | ||||||
|     #endif | #endif | ||||||
|     #if defined(GWBME28012) | #if defined(GWBME28012) | ||||||
|     { |     { | ||||||
|         auto *cfg=creator(api,PRFX2); |         api->addSensor(creator(api,"BME28012")); | ||||||
|         sensors.add(api,cfg); |  | ||||||
|         CHECK_IIC1(); |         CHECK_IIC1(); | ||||||
|         #pragma message "GWBME28012 defined" |         #pragma message "GWBME28012 defined" | ||||||
|     } |     } | ||||||
|     #endif | #endif | ||||||
|     #if defined(GWBME28021) | #if defined(GWBME28021) | ||||||
|     { |     { | ||||||
|         auto *cfg=creator(api,PRFX3); |         api->addSensor(creator(api,"BME28021")); | ||||||
|         sensors.add(api,cfg); |  | ||||||
|         CHECK_IIC2(); |         CHECK_IIC2(); | ||||||
|         #pragma message "GWBME28021 defined" |         #pragma message "GWBME28021 defined" | ||||||
|     } |     } | ||||||
|     #endif | #endif | ||||||
|     #if defined(GWBME28022) | #if defined(GWBME28022) | ||||||
|     { |     { | ||||||
|         auto *cfg=creator(api,PRFX4); |         api->addSensor(creator(api,"BME28022")); | ||||||
|         sensors.add(api,cfg); |  | ||||||
|         CHECK_IIC1(); |         CHECK_IIC1(); | ||||||
|         #pragma message "GWBME28022 defined" |         #pragma message "GWBME28022 defined" | ||||||
|     } |     } | ||||||
|     #endif | #endif | ||||||
|     return creator; |     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 | #else | ||||||
| IICSensorBase::Creator registerBME280(GwApi *api,IICSensorList &sensors){  | SensorBase::Creator registerBME280(GwApi *api){  | ||||||
|     return IICSensorBase::Creator();  |     return SensorBase::Creator();  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| #ifndef _GWBME280_H | #ifndef _GWBME280_H | ||||||
| #define _GWBME280_H | #define _GWBME280_H | ||||||
| #include "GwIicSensors.h" | #include "GwIicSensors.h" | ||||||
| IICSensorBase::Creator registerBME280(GwApi *api,IICSensorList &sensors); | SensorBase::Creator registerBME280(GwApi *api); | ||||||
| #endif | #endif | ||||||
|  | @ -15,11 +15,16 @@ | ||||||
|     #include <Adafruit_BMP280.h> |     #include <Adafruit_BMP280.h> | ||||||
| #endif | #endif | ||||||
| #ifdef _GWBMP280 | #ifdef _GWBMP280 | ||||||
| #define TYPE "BMP280" | 
 | ||||||
| #define PRFX1 TYPE "11" | 
 | ||||||
| #define PRFX2 TYPE "12" | /**
 | ||||||
| #define PRFX3 TYPE "21" |  * we need a forward declaration here as the config list has to go before the | ||||||
| #define PRFX4 TYPE "22" |  * class implementation | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | class BMP280Config; | ||||||
|  | static GwSensorConfigInitializerList<BMP280Config> configs; | ||||||
|  | 
 | ||||||
| class BMP280Config : public IICSensorBase{ | class BMP280Config : public IICSensorBase{ | ||||||
|     public: |     public: | ||||||
|     bool prAct=true; |     bool prAct=true; | ||||||
|  | @ -33,14 +38,13 @@ class BMP280Config : public IICSensorBase{ | ||||||
|     float prOff=0; |     float prOff=0; | ||||||
|     Adafruit_BMP280 *device=nullptr; |     Adafruit_BMP280 *device=nullptr; | ||||||
|     uint32_t sensorId=-1; |     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 isActive(){return prAct||tmAct;} | ||||||
|     virtual bool initDevice(GwApi *api,TwoWire *wire){ |     virtual bool initDevice(GwApi *api,TwoWire *wire){ | ||||||
|         GwLog *logger=api->getLogger();   |         GwLog *logger=api->getLogger();   | ||||||
|         device= new Adafruit_BMP280(wire); |         device= new Adafruit_BMP280(wire); | ||||||
|         if (! device->begin(addr)){ |         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; |             delete device; | ||||||
|             device=nullptr; |             device=nullptr; | ||||||
|             return false; |             return false; | ||||||
|  | @ -52,7 +56,6 @@ class BMP280Config : public IICSensorBase{ | ||||||
|     virtual bool preinit(GwApi * api){ |     virtual bool preinit(GwApi * api){ | ||||||
|         GwLog *logger=api->getLogger(); |         GwLog *logger=api->getLogger(); | ||||||
|         LOG_DEBUG(GwLog::LOG,"%s configured",prefix.c_str()); |         LOG_DEBUG(GwLog::LOG,"%s configured",prefix.c_str()); | ||||||
|         api->addCapability(prefix,"true"); |  | ||||||
|         addPressureXdr(api,*this); |         addPressureXdr(api,*this); | ||||||
|         addTempXdr(api,*this); |         addTempXdr(api,*this); | ||||||
|         return isActive(); |         return isActive(); | ||||||
|  | @ -85,96 +88,90 @@ class BMP280Config : public IICSensorBase{ | ||||||
|             sendN2kEnvironmentalParameters(api, *this, temperature, humidity, computed,counterId); |             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 |     virtual void readConfig(GwConfigHandler *cfg) override | ||||||
|     { |     { | ||||||
|         if (prefix == PRFX1) |         if (ok) return; | ||||||
|         { |         configs.readConfig(this,cfg); | ||||||
|             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; |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 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); |     return new BMP280Config(api,prfx); | ||||||
| }); | }); | ||||||
| IICSensorBase::Creator registerBMP280(GwApi *api,IICSensorList &sensors){ | SensorBase::Creator registerBMP280(GwApi *api){ | ||||||
|     #if defined(GWBMP280) || defined(GWBMP28011) |     #if defined(GWBMP280) || defined(GWBMP28011) | ||||||
|     {    |     {    | ||||||
|         auto *cfg=creator(api,PRFX1); |         api->addSensor(creator(api,"BMP28011")); | ||||||
|         //BMP280Config *cfg=new BMP280Config(api,PRFX1);
 |  | ||||||
|         sensors.add(api,cfg); |  | ||||||
|         CHECK_IIC1(); |         CHECK_IIC1(); | ||||||
|         #pragma message "GWBMP28011 defined" |         #pragma message "GWBMP28011 defined" | ||||||
|     } |     } | ||||||
|     #endif |     #endif | ||||||
|     #if defined(GWBMP28012) |     #if defined(GWBMP28012) | ||||||
|     { |     { | ||||||
|         auto *cfg=creator(api,PRFX2); |         api->addSensor(creator(api,"BMP28012")); | ||||||
|         //BMP280Config *cfg=new BMP280Config(api,PRFX2);
 |  | ||||||
|         sensors.add(api,cfg); |  | ||||||
|         CHECK_IIC1(); |         CHECK_IIC1(); | ||||||
|         #pragma message "GWBMP28012 defined" |         #pragma message "GWBMP28012 defined" | ||||||
|     } |     } | ||||||
|     #endif |     #endif | ||||||
|     #if defined(GWBMP28021) |     #if defined(GWBMP28021) | ||||||
|     { |     { | ||||||
|         auto *cfg=creator(api,PRFX3); |         api->addSensor(creator(api,"BMP28021")); | ||||||
|         //BMP280Config *cfg=new BMP280Config(api,PRFX3);
 |  | ||||||
|         sensors.add(api,cfg); |  | ||||||
|         CHECK_IIC2(); |         CHECK_IIC2(); | ||||||
|         #pragma message "GWBMP28021 defined" |         #pragma message "GWBMP28021 defined" | ||||||
|     } |     } | ||||||
|     #endif |     #endif | ||||||
|     #if defined(GWBMP28022) |     #if defined(GWBMP28022) | ||||||
|     { |     { | ||||||
|         auto *cfg=creator(api,PRFX4); |         api->addSensor(creator(api,"BMP28022")); | ||||||
|         //BMP280Config *cfg=new BMP280Config(api,PRFX4);
 |  | ||||||
|         sensors.add(api,cfg); |  | ||||||
|         CHECK_IIC1(); |         CHECK_IIC1(); | ||||||
|         #pragma message "GWBMP28022 defined" |         #pragma message "GWBMP28022 defined" | ||||||
|     } |     } | ||||||
|     #endif |     #endif | ||||||
|     return creator; |     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 | #else | ||||||
| IICSensorBase::Creator registerBMP280(GwApi *api,IICSensorList &sensors){ | SensorBase::Creator registerBMP280(GwApi *api){ | ||||||
|    return IICSensorBase::Creator(); |    return SensorBase::Creator(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| #ifndef _GWBMP280_H | #ifndef _GWBMP280_H | ||||||
| #define _GWBMP280_H | #define _GWBMP280_H | ||||||
| #include "GwIicSensors.h" | #include "GwIicSensors.h" | ||||||
| IICSensorBase::Creator registerBMP280(GwApi *api,IICSensorList &sensors); | SensorBase::Creator registerBMP280(GwApi *api); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -11,9 +11,9 @@ | ||||||
|     class TwoWire; |     class TwoWire; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| using BusType=TwoWire; | using BUSTYPE=TwoWire; | ||||||
| using IICSensorList=SensorList<BusType>; | using IICSensorList=SensorList; | ||||||
| using IICSensorBase=SensorBase<BusType>; | using IICSensorBase=SensorTemplate<BUSTYPE,SensorBase::IIC>; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| template <class CFG> | template <class CFG> | ||||||
|  |  | ||||||
|  | @ -43,8 +43,7 @@ static std::vector<IICGrove> iicGroveList; | ||||||
| 
 | 
 | ||||||
| void runIicTask(GwApi *api); | void runIicTask(GwApi *api); | ||||||
| 
 | 
 | ||||||
| static IICSensorList sensors; | static void addGroveItems(std::vector<SensorBase::Creator> &creators,GwApi *api, const String &bus,const String &grove, int, int) | ||||||
| static void addGroveItems(std::vector<IICSensorBase::Creator> &creators,GwApi *api, IICSensorList &sensors, const String &bus,const String &grove, int, int) |  | ||||||
| { | { | ||||||
|     GwLog *logger=api->getLogger(); |     GwLog *logger=api->getLogger(); | ||||||
|     for (auto &&init : iicGroveList) |     for (auto &&init : iicGroveList) | ||||||
|  | @ -61,17 +60,18 @@ static void addGroveItems(std::vector<IICSensorBase::Creator> &creators,GwApi *a | ||||||
|             { |             { | ||||||
|                 if (! creator) continue; |                 if (! creator) continue; | ||||||
|                 auto *scfg = creator(api, prfx); |                 auto *scfg = creator(api, prfx); | ||||||
|  |                 if (scfg == nullptr) continue; | ||||||
|                 scfg->readConfig(api->getConfig()); |                 scfg->readConfig(api->getConfig()); | ||||||
|                 if (scfg->ok) |                 if (scfg->ok) | ||||||
|                 { |                 { | ||||||
|                     LOG_DEBUG(GwLog::LOG, "adding %s from grove config", prfx.c_str()); |                     LOG_DEBUG(GwLog::LOG, "adding %s from grove config", prfx.c_str()); | ||||||
|                     sensors.add(api, scfg); |                     api->addSensor(scfg,false); | ||||||
|                     found=true; |                     found=true; | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|                 else |                 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; |                     delete scfg; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -89,19 +89,26 @@ void initIicTask(GwApi *api){ | ||||||
|     #else |     #else | ||||||
|     bool addTask=false; |     bool addTask=false; | ||||||
|     GwConfigHandler *config=api->getConfig(); |     GwConfigHandler *config=api->getConfig(); | ||||||
|     std::vector<IICSensorBase::Creator> creators; |     std::vector<SensorBase::Creator> creators; | ||||||
|     creators.push_back(registerSHT3X(api,sensors)); |     creators.push_back(registerSHT3X(api)); | ||||||
|     creators.push_back(registerQMP6988(api,sensors)); |     creators.push_back(registerQMP6988(api)); | ||||||
|     creators.push_back(registerBME280(api,sensors)); |     creators.push_back(registerBME280(api)); | ||||||
|     creators.push_back(registerBMP280(api,sensors)); |     creators.push_back(registerBMP280(api)); | ||||||
|     #ifdef _GWI_IIC1 |     #ifdef _GWI_IIC1 | ||||||
|         addGroveItems(creators,api,sensors,"1",_GWI_IIC1);     |         addGroveItems(creators,api,"1",_GWI_IIC1);     | ||||||
|     #endif |     #endif | ||||||
|     #ifdef _GWI_IIC2 |     #ifdef _GWI_IIC2 | ||||||
|         addGroveItems(creators,api,sensors,"2",_GWI_IIC2);     |         addGroveItems(creators,api,"2",_GWI_IIC2);     | ||||||
|     #endif |     #endif | ||||||
|     for (auto it=sensors.begin();it != sensors.end();it++){ |     //TODO: ensure that we run after other init tasks...
 | ||||||
|         if ((*it)->preinit(api)) addTask=true; |     int res=-1; | ||||||
|  |     ConfiguredSensors sensorList=api->taskInterfaces()->get<ConfiguredSensors>(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){ |     if (addTask){ | ||||||
|         api->addUserTask(runIicTask,"iicTask",4000); |         api->addUserTask(runIicTask,"iicTask",4000); | ||||||
|  | @ -154,8 +161,11 @@ void runIicTask(GwApi *api){ | ||||||
|     GwLog *logger=api->getLogger(); |     GwLog *logger=api->getLogger(); | ||||||
|     std::map<int,TwoWire *> buses; |     std::map<int,TwoWire *> buses; | ||||||
|     LOG_DEBUG(GwLog::LOG,"iic task started"); |     LOG_DEBUG(GwLog::LOG,"iic task started"); | ||||||
|     for (auto it=sensors.begin();it != sensors.end();it++){ |     int res=-1; | ||||||
|         int busId=(*it)->busId; |     ConfiguredSensors sensorList=api->taskInterfaces()->get<ConfiguredSensors>(res); | ||||||
|  |     for (auto &&it : sensorList.sensors){ | ||||||
|  |         if (it->busType != SensorBase::IIC) continue; | ||||||
|  |         int busId=it->busId; | ||||||
|         auto bus=buses.find(busId); |         auto bus=buses.find(busId); | ||||||
|         if (bus == buses.end()){ |         if (bus == buses.end()){ | ||||||
|             switch (busId) |             switch (busId) | ||||||
|  | @ -175,7 +185,7 @@ void runIicTask(GwApi *api){ | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|             default: |             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; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -184,8 +194,8 @@ void runIicTask(GwApi *api){ | ||||||
|     bool runLoop=false; |     bool runLoop=false; | ||||||
|     GwIntervalRunner timers; |     GwIntervalRunner timers; | ||||||
|     int counterId=api->addCounter("iicsensors"); |     int counterId=api->addCounter("iicsensors"); | ||||||
|     for (auto it=sensors.begin();it != sensors.end();it++){ |     for (auto && cfg: sensorList.sensors){ | ||||||
|         IICSensorBase *cfg=*it; |         if (cfg->busType != SensorBase::IIC) continue; | ||||||
|         auto bus=buses.find(cfg->busId); |         auto bus=buses.find(cfg->busId); | ||||||
|         if (! cfg->isActive()) continue; |         if (! cfg->isActive()) continue; | ||||||
|         if (bus == buses.end()){ |         if (bus == buses.end()){ | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| #ifndef _GWIICTASK_H | #ifndef _GWIICTASK_H | ||||||
| #define _GWIICTASK_H | #define _GWIICTASK_H | ||||||
| #include "GwApi.h" | #include "GwApi.h" | ||||||
|  | #include "GwSensor.h" | ||||||
| void initIicTask(GwApi *api); | void initIicTask(GwApi *api); | ||||||
| DECLARE_INITFUNCTION(initIicTask); | DECLARE_INITFUNCTION_ORDER(initIicTask,GWLATEORDER); | ||||||
| #endif | #endif | ||||||
|  | @ -1,11 +1,10 @@ | ||||||
| #define _IIC_GROOVE_LIST | #define _IIC_GROOVE_LIST | ||||||
| #include "GwQMP6988.h" | #include "GwQMP6988.h" | ||||||
| #ifdef _GWQMP6988 | #ifdef _GWQMP6988 | ||||||
| #define TYPE "QMP6988" | 
 | ||||||
| #define PRFX1 TYPE "11" | class QMP6988Config; | ||||||
| #define PRFX2 TYPE "12" | static GwSensorConfigInitializerList<QMP6988Config> configs; | ||||||
| #define PRFX3 TYPE "21" | 
 | ||||||
| #define PRFX4 TYPE "22" |  | ||||||
| class QMP6988Config : public IICSensorBase{ | class QMP6988Config : public IICSensorBase{ | ||||||
|     public: |     public: | ||||||
|         String prNam="Pressure"; |         String prNam="Pressure"; | ||||||
|  | @ -13,7 +12,7 @@ class QMP6988Config : public IICSensorBase{ | ||||||
|         tN2kPressureSource prSrc=tN2kPressureSource::N2kps_Atmospheric; |         tN2kPressureSource prSrc=tN2kPressureSource::N2kps_Atmospheric; | ||||||
|         float prOff=0; |         float prOff=0; | ||||||
|         QMP6988 *device=nullptr; |         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 isActive(){return prAct;}; | ||||||
|         virtual bool initDevice(GwApi *api,TwoWire *wire){ |         virtual bool initDevice(GwApi *api,TwoWire *wire){ | ||||||
|             if (!isActive()) return false; |             if (!isActive()) return false; | ||||||
|  | @ -31,7 +30,6 @@ class QMP6988Config : public IICSensorBase{ | ||||||
|         virtual bool preinit(GwApi * api){ |         virtual bool preinit(GwApi * api){ | ||||||
|             GwLog *logger=api->getLogger(); |             GwLog *logger=api->getLogger(); | ||||||
|             LOG_DEBUG(GwLog::LOG,"QMP6988 configured"); |             LOG_DEBUG(GwLog::LOG,"QMP6988 configured"); | ||||||
|             api->addCapability(prefix,"true"); |  | ||||||
|             addPressureXdr(api,*this); |             addPressureXdr(api,*this); | ||||||
|             return isActive(); |             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); |             LOG_DEBUG(GwLog::DEBUG,"%s measure %2.0fPa, computed %2.0fPa",prefix.c_str(), pressure,computed); | ||||||
|             sendN2kPressure(api,*this,computed,counterId); |             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){ |         virtual void readConfig(GwConfigHandler *cfg){ | ||||||
|             if (ok) return; |             if (ok) return; | ||||||
|             if (prefix == PRFX1){ |             configs.readConfig(this,cfg); | ||||||
|                 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; |  | ||||||
| 
 |  | ||||||
|         } |         } | ||||||
| }; | }; | ||||||
| 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); |     return new QMP6988Config(api,prfx); | ||||||
| }; | }; | ||||||
| IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors){ | SensorBase::Creator registerQMP6988(GwApi *api){ | ||||||
|     GwLog *logger=api->getLogger(); |     GwLog *logger=api->getLogger(); | ||||||
|     #if defined(GWQMP6988) || defined(GWQMP698811) |     #if defined(GWQMP6988) || defined(GWQMP698811) | ||||||
|     { |     { | ||||||
|         QMP6988Config *scfg=new QMP6988Config(api,PRFX1); |         api->addSensor(new QMP6988Config(api,"QMP698811")); | ||||||
|         sensors.add(api,scfg); |  | ||||||
|         CHECK_IIC1(); |         CHECK_IIC1(); | ||||||
|         #pragma message "GWQMP698811 defined" |         #pragma message "GWQMP698811 defined" | ||||||
|     } |     } | ||||||
|     #endif |     #endif | ||||||
|     #if defined(GWQMP698812) |     #if defined(GWQMP698812) | ||||||
|     { |     { | ||||||
|         QMP6988Config *scfg=new QMP6988Config(api,PRFX2); |         api->addSensor(new QMP6988Config(api,"QMP698812")); | ||||||
|         sensors.add(api,scfg); |  | ||||||
|         CHECK_IIC1(); |         CHECK_IIC1(); | ||||||
|         #pragma message "GWQMP698812 defined" |         #pragma message "GWQMP698812 defined" | ||||||
|     } |     } | ||||||
|     #endif |     #endif | ||||||
|     #if defined(GWQMP698821) |     #if defined(GWQMP698821) | ||||||
|     { |     { | ||||||
|         QMP6988Config *scfg=new QMP6988Config(api,PRFX3); |         api->addSensor(new QMP6988Config(api,"QMP698821")); | ||||||
|         sensors.add(api,scfg); |  | ||||||
|         CHECK_IIC2(); |         CHECK_IIC2(); | ||||||
|         #pragma message "GWQMP698821 defined" |         #pragma message "GWQMP698821 defined" | ||||||
|     } |     } | ||||||
|     #endif |     #endif | ||||||
|     #if defined(GWQMP698822) |     #if defined(GWQMP698822) | ||||||
|     { |     { | ||||||
|         QMP6988Config *scfg=new QMP6988Config(api,PRFX4); |         api->addSensor(new QMP6988Config(api,"QMP698822")); | ||||||
|         sensors.add(api,scfg); |  | ||||||
|         CHECK_IIC2(); |         CHECK_IIC2(); | ||||||
|         #pragma message "GWQMP698822 defined" |         #pragma message "GWQMP698822 defined" | ||||||
|     } |     } | ||||||
|  | @ -119,8 +84,28 @@ IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors){ | ||||||
|     return creator; |     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 | #else | ||||||
|     IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors){ |     SensorBase::Creator registerQMP6988(GwApi *api){ | ||||||
|         return IICSensorBase::Creator(); |         return SensorBase::Creator(); | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|  | @ -16,5 +16,5 @@ | ||||||
| #ifdef _GWQMP6988 | #ifdef _GWQMP6988 | ||||||
|     #include "QMP6988.h" |     #include "QMP6988.h" | ||||||
| #endif | #endif | ||||||
| IICSensorBase::Creator registerQMP6988(GwApi *api,IICSensorList &sensors); | SensorBase::Creator registerQMP6988(GwApi *api); | ||||||
| #endif | #endif | ||||||
|  | @ -1,11 +1,7 @@ | ||||||
| #include "GwSHT3X.h" | #include "GwSHT3X.h" | ||||||
| #ifdef _GWSHT3X | #ifdef _GWSHT3X | ||||||
| #define TYPE "SHT3X" | class SHT3XConfig; | ||||||
| #define PRFX1 TYPE "11" | static GwSensorConfigInitializerList<SHT3XConfig> configs; | ||||||
| #define PRFX2 TYPE "12" |  | ||||||
| #define PRFX3 TYPE "21" |  | ||||||
| #define PRFX4 TYPE "22" |  | ||||||
| 
 |  | ||||||
| class SHT3XConfig : public IICSensorBase{ | class SHT3XConfig : public IICSensorBase{ | ||||||
|     public: |     public: | ||||||
|     String tmNam; |     String tmNam; | ||||||
|  | @ -15,8 +11,7 @@ class SHT3XConfig : public IICSensorBase{ | ||||||
|     tN2kHumiditySource huSrc; |     tN2kHumiditySource huSrc; | ||||||
|     tN2kTempSource tmSrc; |     tN2kTempSource tmSrc; | ||||||
|     SHT3X *device=nullptr; |     SHT3X *device=nullptr; | ||||||
|     SHT3XConfig(GwApi *api,const String &prefix): |     using IICSensorBase::IICSensorBase; | ||||||
|         SensorBase(TYPE,api,prefix){} |  | ||||||
|     virtual bool isActive(){ |     virtual bool isActive(){ | ||||||
|         return tmAct || huAct; |         return tmAct || huAct; | ||||||
|     } |     } | ||||||
|  | @ -31,7 +26,6 @@ class SHT3XConfig : public IICSensorBase{ | ||||||
|     virtual bool preinit(GwApi * api){ |     virtual bool preinit(GwApi * api){ | ||||||
|         GwLog *logger=api->getLogger(); |         GwLog *logger=api->getLogger(); | ||||||
|         LOG_DEBUG(GwLog::LOG,"%s configured",prefix.c_str()); |         LOG_DEBUG(GwLog::LOG,"%s configured",prefix.c_str()); | ||||||
|         api->addCapability(prefix,"true"); |  | ||||||
|         addHumidXdr(api,*this); |         addHumidXdr(api,*this); | ||||||
|         addTempXdr(api,*this); |         addTempXdr(api,*this); | ||||||
|         return isActive(); |         return isActive(); | ||||||
|  | @ -62,83 +56,43 @@ class SHT3XConfig : public IICSensorBase{ | ||||||
|             LOG_DEBUG(GwLog::DEBUG, "unable to query %s: %d",prefix.c_str(), rt); |             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){ |     virtual void readConfig(GwConfigHandler *cfg){ | ||||||
|         if (ok) return; |         if (ok) return; | ||||||
|         if (prefix == PRFX1){ |         configs.readConfig(this,cfg); | ||||||
|             busId=1; |         return; | ||||||
|             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; |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 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); |     return new SHT3XConfig(api,prfx); | ||||||
| }; | }; | ||||||
| IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors){ | SensorBase::Creator registerSHT3X(GwApi *api){ | ||||||
|     GwLog *logger=api->getLogger(); |     GwLog *logger=api->getLogger(); | ||||||
|     #if defined(GWSHT3X) || defined (GWSHT3X11) |     #if defined(GWSHT3X) || defined (GWSHT3X11) | ||||||
|     { |     { | ||||||
|         auto *scfg=creator(api,PRFX1); |         api->addSensor(creator(api,"SHT3X11")); | ||||||
|         sensors.add(api,scfg); |  | ||||||
|         CHECK_IIC1(); |         CHECK_IIC1(); | ||||||
|         #pragma message "GWSHT3X11 defined" |         #pragma message "GWSHT3X11 defined" | ||||||
|     } |     } | ||||||
|     #endif |     #endif | ||||||
|     #if defined(GWSHT3X12) |     #if defined(GWSHT3X12) | ||||||
|     { |     { | ||||||
|         auto *scfg=creator(api,PRFX2); |         api->addSensor(creator(api,"SHT3X12")); | ||||||
|         sensors.add(api,scfg); |  | ||||||
|         CHECK_IIC1(); |         CHECK_IIC1(); | ||||||
|         #pragma message "GWSHT3X12 defined" |         #pragma message "GWSHT3X12 defined" | ||||||
|     } |     } | ||||||
|     #endif |     #endif | ||||||
|     #if defined(GWSHT3X21) |     #if defined(GWSHT3X21) | ||||||
|     { |     { | ||||||
|         auto *scfg=creator(api,PRFX3); |         api->addSensor(creator(api,"SHT3X21")); | ||||||
|         sensors.add(api,scfg); |  | ||||||
|         CHECK_IIC2(); |         CHECK_IIC2(); | ||||||
|         #pragma message "GWSHT3X21 defined" |         #pragma message "GWSHT3X21 defined" | ||||||
|     } |     } | ||||||
|     #endif |     #endif | ||||||
|     #if defined(GWSHT3X22) |     #if defined(GWSHT3X22) | ||||||
|     { |     { | ||||||
|         auto *scfg=creator(api,PRFX4); |         api->addSensor(creator(api,"SHT3X22")); | ||||||
|         sensors.add(api,scfg); |  | ||||||
|         CHECK_IIC2(); |         CHECK_IIC2(); | ||||||
|         #pragma message "GWSHT3X22 defined" |         #pragma message "GWSHT3X22 defined" | ||||||
|     } |     } | ||||||
|  | @ -146,9 +100,36 @@ IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors){ | ||||||
|     return creator; |     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 | #else | ||||||
| IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors){ | SensorBase::Creator registerSHT3X(GwApi *api){ | ||||||
|     return IICSensorBase::Creator(); |     return SensorBase::Creator(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -16,5 +16,5 @@ | ||||||
| #ifdef _GWSHT3X | #ifdef _GWSHT3X | ||||||
|     #include "SHT3X.h" |     #include "SHT3X.h" | ||||||
| #endif | #endif | ||||||
| IICSensorBase::Creator registerSHT3X(GwApi *api,IICSensorList &sensors); | SensorBase::Creator registerSHT3X(GwApi *api); | ||||||
| #endif | #endif | ||||||
|  | @ -11,16 +11,6 @@ build_flags= | ||||||
|     -D M5_CAN_KIT |     -D M5_CAN_KIT | ||||||
|     ${env.build_flags} |     ${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] | [env:m5stack-atom-bme280] | ||||||
| extends = sensors | extends = sensors | ||||||
|  | @ -55,6 +45,7 @@ lib_deps = | ||||||
|     ${sensors.lib_deps} |     ${sensors.lib_deps} | ||||||
| build_flags= | build_flags= | ||||||
|     -D GWBMP280G1 |     -D GWBMP280G1 | ||||||
|  |     -D GWSHT3X11 | ||||||
|     -D M5_GROOVEIIC |     -D M5_GROOVEIIC | ||||||
|     -D M5_CAN_KIT |     -D M5_CAN_KIT | ||||||
|     ${env.build_flags} |     ${env.build_flags} | ||||||
|  |  | ||||||
|  | @ -189,6 +189,7 @@ private: | ||||||
|         if (N2kIsNA(v)) return N2kInt8NA; |         if (N2kIsNA(v)) return N2kInt8NA; | ||||||
|         return v; |         return v; | ||||||
|     } |     } | ||||||
|  |      | ||||||
|     void convertXDR(const SNMEA0183Msg &msg){ |     void convertXDR(const SNMEA0183Msg &msg){ | ||||||
|         XdrMappingList foundMappings; |         XdrMappingList foundMappings; | ||||||
|         for (int offset=0;offset <= (msg.FieldCount()-4);offset+=4){ |         for (int offset=0;offset <= (msg.FieldCount()-4);offset+=4){ | ||||||
|  | @ -199,7 +200,19 @@ private: | ||||||
|             String unit=msg.Field(offset+2); |             String unit=msg.Field(offset+2); | ||||||
|             String transducerName=msg.Field(offset+3); |             String transducerName=msg.Field(offset+3); | ||||||
|             GwXDRFoundMapping found=xdrMappings->getMapping(transducerName,type,unit); |             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); |             value=found.valueFromXdr(value); | ||||||
|             if (!boatData->update(value,msg.sourceId,&found)) continue; |             if (!boatData->update(value,msg.sourceId,&found)) continue; | ||||||
|             LOG_DEBUG(GwLog::DEBUG+1,"found mapped XDR %s:%s, value %f", |             LOG_DEBUG(GwLog::DEBUG+1,"found mapped XDR %s:%s, value %f", | ||||||
|  | @ -307,7 +320,7 @@ private: | ||||||
|             return;    |             return;    | ||||||
|         } |         } | ||||||
|         tN2kMsg n2kMsg; |         tN2kMsg n2kMsg; | ||||||
|         if (boatData->XTE->update(rmb.xte,msg.sourceId)){ |         if (updateDouble(boatData->XTE,rmb.xte,msg.sourceId)){ | ||||||
|             tN2kXTEMode mode=N2kxtem_Autonomous; |             tN2kXTEMode mode=N2kxtem_Autonomous; | ||||||
|             if (msg.FieldCount() > 13){ |             if (msg.FieldCount() > 13){ | ||||||
|                 const char *modeChar=msg.Field(13); |                 const char *modeChar=msg.Field(13); | ||||||
|  | @ -318,10 +331,10 @@ private: | ||||||
|         } |         } | ||||||
|         uint8_t destinationId=getWaypointId(rmb.destID); |         uint8_t destinationId=getWaypointId(rmb.destID); | ||||||
|         uint8_t sourceId=getWaypointId(rmb.originID); |         uint8_t sourceId=getWaypointId(rmb.originID); | ||||||
|         if (boatData->DTW->update(rmb.dtw,msg.sourceId) |         if (updateDouble(boatData->DTW,rmb.dtw,msg.sourceId) | ||||||
|             && boatData->BTW->update(rmb.btw,msg.sourceId) |             && updateDouble(boatData->BTW,rmb.btw,msg.sourceId) | ||||||
|             && boatData->WPLat->update(rmb.latitude,msg.sourceId) |             && updateDouble(boatData->WPLat,rmb.latitude,msg.sourceId) | ||||||
|             && boatData->WPLon->update(rmb.longitude,msg.sourceId) |             && updateDouble(boatData->WPLon,rmb.longitude,msg.sourceId) | ||||||
|             ){ |             ){ | ||||||
|             SetN2kNavigationInfo(n2kMsg,1,rmb.dtw,N2khr_true, |             SetN2kNavigationInfo(n2kMsg,1,rmb.dtw,N2khr_true, | ||||||
|                 false, |                 false, | ||||||
|  |  | ||||||
|  | @ -3,14 +3,26 @@ | ||||||
| 
 | 
 | ||||||
| class GwSynchronized{ | class GwSynchronized{ | ||||||
|     private: |     private: | ||||||
|         SemaphoreHandle_t *locker; |         SemaphoreHandle_t locker=nullptr; | ||||||
|  |         void lock(){ | ||||||
|  |             if (locker != nullptr) xSemaphoreTake(locker, portMAX_DELAY); | ||||||
|  |         } | ||||||
|     public: |     public: | ||||||
|  |         /**
 | ||||||
|  |          * deprecated | ||||||
|  |          * as SemaphoreHandle_t is already a pointer just use this directly | ||||||
|  |         */ | ||||||
|         GwSynchronized(SemaphoreHandle_t *locker){ |         GwSynchronized(SemaphoreHandle_t *locker){ | ||||||
|  |             if (locker == nullptr) return; | ||||||
|  |             this->locker=*locker; | ||||||
|  |             lock(); | ||||||
|  |         } | ||||||
|  |         GwSynchronized(SemaphoreHandle_t locker){ | ||||||
|             this->locker=locker; |             this->locker=locker; | ||||||
|             if (locker != nullptr) xSemaphoreTake(*locker, portMAX_DELAY); |             lock(); | ||||||
|         } |         } | ||||||
|         ~GwSynchronized(){ |         ~GwSynchronized(){ | ||||||
|             if (locker != nullptr) xSemaphoreGive(*locker); |             if (locker != nullptr) xSemaphoreGive(locker); | ||||||
|         } |         } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  |     } | ||||||
|  | @ -14,42 +14,124 @@ | ||||||
| */ | */ | ||||||
| #ifndef _GWSENSORS_H | #ifndef _GWSENSORS_H | ||||||
| #define _GWSENSORS_H | #define _GWSENSORS_H | ||||||
| #include "GwApi.h" | #include "GwConfigItem.h" | ||||||
| #include "GwLog.h" | #include <Arduino.h> | ||||||
| template<typename BUS> | #include <memory> | ||||||
|  | #include <vector> | ||||||
|  | class GwApi; | ||||||
|  | class GwConfigHandler; | ||||||
| class SensorBase{ | class SensorBase{ | ||||||
|     public: |     public: | ||||||
|  |     using BusType=enum{ | ||||||
|  |         IIC=0, | ||||||
|  |         SPI=1, | ||||||
|  |         UNKNOWN=-1 | ||||||
|  |     }; | ||||||
|  |     using Ptr=std::shared_ptr<SensorBase>; | ||||||
|  |     BusType busType=BusType::UNKNOWN; | ||||||
|     int busId=0; |     int busId=0; | ||||||
|     int iid=99; //N2K instanceId
 |     int iid=99; //N2K instanceId
 | ||||||
|     int addr=-1; |     int addr=-1; | ||||||
|     const String prefix; |     const String prefix; | ||||||
|     const String type; |  | ||||||
|     long intv=0; |     long intv=0; | ||||||
|     bool ok=false; |     bool ok=false; | ||||||
|     virtual void readConfig(GwConfigHandler *cfg)=0; |     SensorBase(BusType bt,GwApi *api,const String &prfx) | ||||||
|     SensorBase(const String &tn,GwApi *api,const String &prfx):type(tn),prefix(prfx){ |         :busType(bt),prefix(prfx){ | ||||||
|     } |     } | ||||||
|     using Creator=std::function<SensorBase<BUS> *(GwApi *api,const String &prfx)>; |     using Creator=std::function<SensorBase *(GwApi *api,const String &prfx)>; | ||||||
|     virtual bool isActive(){return false;}; |     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 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 ~SensorBase(){} | ||||||
|  |     virtual void readConfig(GwConfigHandler *cfg)=0; | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | template<typename BUS,SensorBase::BusType bt> | ||||||
|  | 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<typename BUS> | 
 | ||||||
| class SensorList : public std::vector<SensorBase<BUS>*>{ | class SensorList : public std::vector<SensorBase::Ptr>{ | ||||||
|     public: |     public: | ||||||
|     void add(GwApi *api, SensorBase<BUS> *sensor){ |     void add(SensorBase::Ptr sensor); | ||||||
|         sensor->readConfig(api->getConfig()); |     using std::vector<SensorBase::Ptr>::vector; | ||||||
|         api->getLogger()->logDebug(GwLog::LOG,"configured sensor %s, status %d",sensor->prefix.c_str(),(int)sensor->ok); |  | ||||||
|         this->push_back(sensor); |  | ||||||
|     } |  | ||||||
|     using std::vector<SensorBase<BUS>*>::vector; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * helper classes for a simplified sensor configuration | ||||||
|  |  * by creating a list of type GwSensorConfigInitializerList<SensorClass> 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 <typename T> | ||||||
|  | class GwSensorConfig{ | ||||||
|  |     public: | ||||||
|  |     using ReadConfig=std::function<void(T*,GwConfigHandler*)>; | ||||||
|  |     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<typename T> | ||||||
|  | class GwSensorConfigInitializer : public GwInitializer<GwSensorConfig<T>>{ | ||||||
|  |     public: | ||||||
|  |     using GwInitializer<GwSensorConfig<T>>::GwInitializer; | ||||||
|  | }; | ||||||
|  | template<typename T> | ||||||
|  | class GwSensorConfigInitializerList : public GwInitializer<GwSensorConfig<T>>::List{ | ||||||
|  |     public: | ||||||
|  |     using GwInitializer<GwSensorConfig<T>>::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<type> __init ## type ## prefix(list,GwSensorConfig<type>(#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) \ | #define CFG_GET(name,prefix) \ | ||||||
|     cfg->getValue(name, GwConfigDefinitions::prefix ## name) |     cfg->getValue(name, GwConfigDefinitions::prefix ## name) | ||||||
|  | 
 | ||||||
|      |      | ||||||
| #endif | #endif | ||||||
|  | @ -63,6 +63,7 @@ GwSerial::~GwSerial() | ||||||
| { | { | ||||||
|     delete buffer; |     delete buffer; | ||||||
|     if (readBuffer) delete readBuffer; |     if (readBuffer) delete readBuffer; | ||||||
|  |     if (lock != nullptr) vSemaphoreDelete(lock); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| String GwSerial::getMode(){ | String GwSerial::getMode(){ | ||||||
|  | @ -87,10 +88,14 @@ size_t GwSerial::enqueue(const uint8_t *data, size_t len, bool partial) | ||||||
| } | } | ||||||
| GwBuffer::WriteStatus GwSerial::write(){ | GwBuffer::WriteStatus GwSerial::write(){ | ||||||
|     if (! isInitialized()) return GwBuffer::ERROR; |     if (! isInitialized()) return GwBuffer::ERROR; | ||||||
|     size_t numWrite=availableForWrite();           |     size_t rt=0; | ||||||
|     size_t rt=buffer->fetchData(numWrite,[](uint8_t *buffer,size_t len, void *p){ |     { | ||||||
|         return ((GwSerial *)p)->stream->write(buffer,len); |         GWSYNCHRONIZED(lock); | ||||||
|     },this); |         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){ |     if (rt != 0){ | ||||||
|         LOG_DEBUG(GwLog::DEBUG+1,"Serial %d write %d",id,rt); |         LOG_DEBUG(GwLog::DEBUG+1,"Serial %d write %d",id,rt); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| #include "GwLog.h" | #include "GwLog.h" | ||||||
| #include "GwBuffer.h" | #include "GwBuffer.h" | ||||||
| #include "GwChannelInterface.h" | #include "GwChannelInterface.h" | ||||||
|  | #include "GwSynchronized.h" | ||||||
| #if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 | #if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 | ||||||
|   #include "hal/usb_serial_jtag_ll.h" |   #include "hal/usb_serial_jtag_ll.h" | ||||||
| #endif | #endif | ||||||
|  | @ -26,8 +27,12 @@ class GwSerial : public GwChannelInterface{ | ||||||
|         virtual long getFlushTimeout(){return 2000;} |         virtual long getFlushTimeout(){return 2000;} | ||||||
|         virtual int availableForWrite()=0; |         virtual int availableForWrite()=0; | ||||||
|         int type=0; |         int type=0; | ||||||
|  |         SemaphoreHandle_t lock=nullptr; | ||||||
|     public: |     public: | ||||||
|         GwSerial(GwLog *logger,Stream *stream,int id,int type,bool allowRead=true); |         GwSerial(GwLog *logger,Stream *stream,int id,int type,bool allowRead=true); | ||||||
|  |         void enableWriteLock(){ | ||||||
|  |             lock=xSemaphoreCreateMutex(); | ||||||
|  |         } | ||||||
|         virtual ~GwSerial(); |         virtual ~GwSerial(); | ||||||
|         bool isInitialized(); |         bool isInitialized(); | ||||||
|         virtual size_t sendToClients(const char *buf,int sourceId,bool partial=false); |         virtual size_t sendToClients(const char *buf,int sourceId,bool partial=false); | ||||||
|  | @ -94,6 +99,7 @@ template<typename T> | ||||||
|                 if (c->isConnected()){ |                 if (c->isConnected()){ | ||||||
|                     //this retriggers the ISR
 |                     //this retriggers the ISR
 | ||||||
|                     usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); |                     usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); | ||||||
|  |                     usb_serial_jtag_ll_txfifo_flush(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             return rt; |             return rt; | ||||||
|  |  | ||||||
|  | @ -246,7 +246,7 @@ void GwTcpClient::resolveHost(String host) | ||||||
| { | { | ||||||
|     LOG_DEBUG(GwLog::LOG,"start resolving %s",host.c_str()); |     LOG_DEBUG(GwLog::LOG,"start resolving %s",host.c_str()); | ||||||
|     { |     { | ||||||
|         GWSYNCHRONIZED(&locker); |         GWSYNCHRONIZED(locker); | ||||||
|         resolvedAddress.resolved = false; |         resolvedAddress.resolved = false; | ||||||
|     } |     } | ||||||
|     state = C_RESOLVING; |     state = C_RESOLVING; | ||||||
|  | @ -283,12 +283,12 @@ void GwTcpClient::resolveHost(String host) | ||||||
| void GwTcpClient::setResolved(IPAddress addr, bool valid){ | void GwTcpClient::setResolved(IPAddress addr, bool valid){ | ||||||
|     LOG_DEBUG(GwLog::LOG,"setResolved %s, valid=%s", |     LOG_DEBUG(GwLog::LOG,"setResolved %s, valid=%s", | ||||||
|         addr.toString().c_str(),(valid?"true":"false")); |         addr.toString().c_str(),(valid?"true":"false")); | ||||||
|     GWSYNCHRONIZED(&locker); |     GWSYNCHRONIZED(locker); | ||||||
|     resolvedAddress.address=addr; |     resolvedAddress.address=addr; | ||||||
|     resolvedAddress.resolved=valid; |     resolvedAddress.resolved=valid; | ||||||
|     state=C_RESOLVED; |     state=C_RESOLVED; | ||||||
| } | } | ||||||
| GwTcpClient::ResolvedAddress GwTcpClient::getResolved(){ | GwTcpClient::ResolvedAddress GwTcpClient::getResolved(){ | ||||||
|     GWSYNCHRONIZED(&locker); |     GWSYNCHRONIZED(locker); | ||||||
|     return resolvedAddress; |     return resolvedAddress; | ||||||
| } | } | ||||||
|  | @ -21,76 +21,23 @@ | ||||||
| #define CHECK_BUS(BUS) \ | #define CHECK_BUS(BUS) \ | ||||||
|     checkDef("missing config for " #BUS,GW ## BUS ## _CLK ,GW ## BUS ## _MISO); |     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<GWDMS22B> configs; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class GWDMS22B : public SSISensor{ | class GWDMS22B : public SSISensor{ | ||||||
|  |     public: | ||||||
|     int zero=2047; |     int zero=2047; | ||||||
|     bool invt=false; |     bool invt=false; | ||||||
|     String zeroConfigName; |     String zeroConfigName; | ||||||
|     public: |     GWDMS22B(GwApi *api,const String &prfx):SSISensor(api,prfx){} | ||||||
|     GWDMS22B(GwApi *api,const String &prfx, int host):SSISensor("DMS22B",api,prfx,host){} |  | ||||||
|     virtual bool preinit(GwApi * api){ |     virtual bool preinit(GwApi * api){ | ||||||
|         GwLog *logger=api->getLogger(); |         GwLog *logger=api->getLogger(); | ||||||
|         LOG_DEBUG(GwLog::LOG,"DMS22B configured, prefix=%s, intv=%f, active=%d",prefix.c_str(),fintv,(int)act); |         LOG_DEBUG(GwLog::LOG,"DMS22B configured, prefix=%s, intv=%f, active=%d",prefix.c_str(),fintv,(int)act); | ||||||
|         api->addCapability(prefix,"true"); |  | ||||||
|         return act; |         return act; | ||||||
|     } |     } | ||||||
|     virtual void measure(GwApi * api,BusType *bus, int counterId){ |     virtual void measure(GwApi * api,BUSTYPE *bus, int counterId){ | ||||||
|         GwLog *logger=api->getLogger(); |         GwLog *logger=api->getLogger(); | ||||||
|         uint32_t value=0; |         uint32_t value=0; | ||||||
|         esp_err_t res=readData(value); |         esp_err_t res=readData(value); | ||||||
|  | @ -106,34 +53,84 @@ class GWDMS22B : public SSISensor{ | ||||||
|         api->increment(counterId,prefix); |         api->increment(counterId,prefix); | ||||||
|         api->setCalibrationValue(zeroConfigName,(double)value); |         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){ |     virtual void readConfig(GwConfigHandler *cfg){ | ||||||
|         DMS22B(DMS22B11); |         if (ok) return; | ||||||
|         DMS22B(DMS22B12); |         configs.readConfig(this,cfg); | ||||||
|         DMS22B(DMS22B21); |  | ||||||
|         DMS22B(DMS22B22); |  | ||||||
|         intv=1000*fintv; |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void registerDMS22B(GwApi *api,SpiSensorList &sensors){ | #define ADD22B(PRFX,BUS) \ | ||||||
|     ADD22B11 |         {\ | ||||||
|     ADD22B12 |         CHECK_BUS(BUS); \ | ||||||
|     ADD22B21 |         GWDMS22B *dms=new GWDMS22B(api,#PRFX);\ | ||||||
|     ADD22B22 |         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); | ||||||
|  |  | ||||||
|  | @ -18,5 +18,5 @@ SSI sensor DMS22B - https://www.mouser.de/datasheet/2/54/bour_s_a0011704065_1-22 | ||||||
| #ifndef _GWDMS22B_H | #ifndef _GWDMS22B_H | ||||||
| #define _GWDMS22B_H | #define _GWDMS22B_H | ||||||
| #include "GwSpiSensor.h" | #include "GwSpiSensor.h" | ||||||
| void registerDMS22B(GwApi *api,SpiSensorList &sensors); | void registerDMS22B(GwApi *api); | ||||||
| #endif | #endif | ||||||
|  | @ -16,7 +16,7 @@ | ||||||
| #ifndef _GWSPISENSOR_H | #ifndef _GWSPISENSOR_H | ||||||
| #define _GWSPISENSOR_H | #define _GWSPISENSOR_H | ||||||
| #include <driver/spi_master.h> | #include <driver/spi_master.h> | ||||||
| #include "GwSensor.h" | #include "GwApi.h" | ||||||
| #include <memory> | #include <memory> | ||||||
| 
 | 
 | ||||||
| class SPIBus{ | class SPIBus{ | ||||||
|  | @ -48,7 +48,7 @@ class SPIBus{ | ||||||
|     spi_host_device_t host() const { return hd;}  |     spi_host_device_t host() const { return hd;}  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| using BusType=SPIBus; | using BUSTYPE=SPIBus; | ||||||
| 
 | 
 | ||||||
| class SSIDevice{ | class SSIDevice{ | ||||||
|     spi_device_handle_t spi; |     spi_device_handle_t spi; | ||||||
|  | @ -90,15 +90,16 @@ class SSIDevice{ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SSISensor : public SensorBase<BusType>{ | class SSISensor : public SensorTemplate<BUSTYPE,SensorBase::SPI>{ | ||||||
|     std::unique_ptr<SSIDevice> device; |     std::unique_ptr<SSIDevice> device; | ||||||
|     protected: |     public: | ||||||
|     int bits=12; |     int bits=12; | ||||||
|     int mask=0xffff; |     int mask=0xffff; | ||||||
|     int cs=-1; |     int cs=-1; | ||||||
|     int clock=0; |     int clock=0; | ||||||
|     bool act=false; |     bool act=false; | ||||||
|     float fintv=0; |     float fintv=0; | ||||||
|  |     protected: | ||||||
|     virtual bool initSSI(GwLog*logger,const SPIBus *bus, |     virtual bool initSSI(GwLog*logger,const SPIBus *bus, | ||||||
|         int clock,int cs, int bits){ |         int clock,int cs, int bits){ | ||||||
|             mask= (1 << bits)-1; |             mask= (1 << bits)-1; | ||||||
|  | @ -125,17 +126,16 @@ class SSISensor : public SensorBase<BusType>{ | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     public: |     public: | ||||||
|     SSISensor(const String &type,GwApi *api,const String &prfx, int host):SensorBase(type,api,prfx) |     SSISensor(GwApi *api,const String &prfx):SensorTemplate<BUSTYPE,SensorBase::SPI>(api,prfx) | ||||||
|     { |     { | ||||||
|         busId=host; |  | ||||||
|     } |     } | ||||||
|     virtual bool isActive(){return act;}; |     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); |         return initSSI(api->getLogger(),bus, clock,cs,bits); | ||||||
|     }; |     }; | ||||||
|      |      | ||||||
| }; | }; | ||||||
| using SpiSensorList=SensorList<BusType>; | using SpiSensorList=SensorList; | ||||||
| #define GWSPI1_HOST SPI2_HOST | #define GWSPI1_HOST SPI2_HOST | ||||||
| #define GWSPI2_HOST SPI3_HOST | #define GWSPI2_HOST SPI3_HOST | ||||||
| #endif | #endif | ||||||
|  | @ -21,8 +21,6 @@ | ||||||
| static SPIBus bus1(GWSPI1_HOST); | static SPIBus bus1(GWSPI1_HOST); | ||||||
| static SPIBus bus2(GWSPI2_HOST); | static SPIBus bus2(GWSPI2_HOST); | ||||||
| 
 | 
 | ||||||
| static SpiSensorList sensors; |  | ||||||
| 
 |  | ||||||
| #ifdef GWSPI1_CLK | #ifdef GWSPI1_CLK | ||||||
| static const int spi1clk=GWSPI1_CLK; | static const int spi1clk=GWSPI1_CLK; | ||||||
| #else | #else | ||||||
|  | @ -57,8 +55,11 @@ static const int spi2mosi=-1; | ||||||
| 
 | 
 | ||||||
| void runSpiTask(GwApi *api){ | void runSpiTask(GwApi *api){ | ||||||
|     GwLog *logger=api->getLogger(); |     GwLog *logger=api->getLogger(); | ||||||
|  |     int res=-1; | ||||||
|  |     ConfiguredSensors sensorList=api->taskInterfaces()->get<ConfiguredSensors>(res); | ||||||
|     std::map<int,SPIBus *> buses; |     std::map<int,SPIBus *> buses; | ||||||
|     for (auto && sensor:sensors){ |     for (auto && sensor: sensorList.sensors){ | ||||||
|  |         if (sensor->busType != SensorBase::BusType::SPI) continue; | ||||||
|         int busId=sensor->busId; |         int busId=sensor->busId; | ||||||
|         auto bus=buses.find(busId); |         auto bus=buses.find(busId); | ||||||
|         if (bus == buses.end()){ |         if (bus == buses.end()){ | ||||||
|  | @ -93,7 +94,7 @@ void runSpiTask(GwApi *api){ | ||||||
|     bool runLoop=false; |     bool runLoop=false; | ||||||
|     GwIntervalRunner timers; |     GwIntervalRunner timers; | ||||||
|     int counterId=api->addCounter("spisensors"); |     int counterId=api->addCounter("spisensors"); | ||||||
|     for (auto && sensor:sensors){ |     for (auto && sensor: sensorList.sensors){ | ||||||
|         if (!sensor->isActive()) continue; |         if (!sensor->isActive()) continue; | ||||||
|         auto bus=buses.find(sensor->busId); |         auto bus=buses.find(sensor->busId); | ||||||
|         if (bus == buses.end()){ |         if (bus == buses.end()){ | ||||||
|  | @ -122,10 +123,16 @@ void runSpiTask(GwApi *api){ | ||||||
| 
 | 
 | ||||||
| void initSpiTask(GwApi *api){ | void initSpiTask(GwApi *api){ | ||||||
|     GwLog *logger=api->getLogger(); |     GwLog *logger=api->getLogger(); | ||||||
|     registerDMS22B(api,sensors); |     int res=-1; | ||||||
|  |     registerDMS22B(api); | ||||||
|  |     ConfiguredSensors sensorList=api->taskInterfaces()->get<ConfiguredSensors>(res); | ||||||
|     bool addTask=false; |     bool addTask=false; | ||||||
|     for (auto && sensor:sensors){ |     for (auto && sensor:sensorList.sensors){ | ||||||
|         if (sensor->preinit(api)) addTask=true; |         if (sensor->busType != SensorBase::BusType::SPI) continue; | ||||||
|  |         if (sensor->preinit(api)) { | ||||||
|  |             api->addCapability(sensor->prefix,"true"); | ||||||
|  |             addTask=true; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     if (addTask){ |     if (addTask){ | ||||||
|         api->addUserTask(runSpiTask,"spiTask",3000);    |         api->addUserTask(runSpiTask,"spiTask",3000);    | ||||||
|  |  | ||||||
|  | @ -16,5 +16,5 @@ | ||||||
| #define _GWSPITASK_H | #define _GWSPITASK_H | ||||||
| #include "GwApi.h" | #include "GwApi.h" | ||||||
| void initSpiTask(GwApi *api); | void initSpiTask(GwApi *api); | ||||||
| DECLARE_INITFUNCTION(initSpiTask); | DECLARE_INITFUNCTION_ORDER(initSpiTask,GWLATEORDER); | ||||||
| #endif | #endif | ||||||
|  | @ -1,11 +1,12 @@ | ||||||
| #define DECLARE_USERTASK(task) GwUserTaskDef __##task##__(task,#task); | #define DECLARE_USERTASK(task) GwUserTaskDef __##task##__(task,#task); | ||||||
| #define DECLARE_USERTASK_PARAM(task,...) GwUserTaskDef __##task##__(task,#task,__VA_ARGS__); | #define DECLARE_USERTASK_PARAM(task,...) GwUserTaskDef __##task##__(task,#task,__VA_ARGS__); | ||||||
| #define DECLARE_INITFUNCTION(task) GwInitTask __Init##task##__(task,#task); | #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_CAPABILITY(name,value) GwUserCapability __CAP##name##__(#name,#value); | ||||||
| #define DECLARE_STRING_CAPABILITY(name,value) GwUserCapability __CAP##name##__(#name,value);  | #define DECLARE_STRING_CAPABILITY(name,value) GwUserCapability __CAP##name##__(#name,value);  | ||||||
| #define DECLARE_TASKIF(type) \ | #define DECLARE_TASKIF(type) \ | ||||||
|     DECLARE_TASKIF_IMPL(type) \ |     DECLARE_TASKIF_IMPL(type) \ | ||||||
|     GwIreg __register##type(__FILE__,#type) |     static int __taskInterface##type=0; //avoid duplicate declarations
 | ||||||
| 
 | 
 | ||||||
| #include "GwUserCode.h" | #include "GwUserCode.h" | ||||||
| #include "GwSynchronized.h" | #include "GwSynchronized.h" | ||||||
|  | @ -28,45 +29,6 @@ bool taskExists(V &list, const String &name){ | ||||||
|     } |     } | ||||||
|     return false; |     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<String,RegEntry>; |  | ||||||
| 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{ | class GwUserTaskDef{ | ||||||
|     public: |     public: | ||||||
|         GwUserTaskDef(TaskFunction_t task,String name, int stackSize=2000){ |         GwUserTaskDef(TaskFunction_t task,String name, int stackSize=2000){ | ||||||
|  | @ -82,8 +44,8 @@ class GwInitTask{ | ||||||
|         GwInitTask(TaskFunction_t task, String name){ |         GwInitTask(TaskFunction_t task, String name){ | ||||||
|             initTasks.push_back(GwUserTask(name,task)); |             initTasks.push_back(GwUserTask(name,task)); | ||||||
|         } |         } | ||||||
|         GwInitTask(GwUserTaskFunction task, String name){ |         GwInitTask(GwUserTaskFunction task, String name,int order=0){ | ||||||
|             initTasks.push_back(GwUserTask(name,task)); |             initTasks.push_back(GwUserTask(name,task,GwUserTask::DEFAULT_STACKSIZE,order)); | ||||||
|         } |         } | ||||||
| }; | }; | ||||||
| class GwUserCapability{ | class GwUserCapability{ | ||||||
|  | @ -112,21 +74,8 @@ class TaskInterfacesStorage{ | ||||||
|             logger(l){ |             logger(l){ | ||||||
|                 lock=xSemaphoreCreateMutex(); |                 lock=xSemaphoreCreateMutex(); | ||||||
|             } |             } | ||||||
|         bool set(const String &file, const String &name, const String &task,GwApi::TaskInterfaces::Ptr v){ |         bool set(const String &name, GwApi::TaskInterfaces::Ptr v){ | ||||||
|             GWSYNCHRONIZED(&lock); |             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; |  | ||||||
|             } |  | ||||||
|             auto vit=values.find(name); |             auto vit=values.find(name); | ||||||
|             if (vit != values.end()){ |             if (vit != values.end()){ | ||||||
|                 vit->second.updates++; |                 vit->second.updates++; | ||||||
|  | @ -141,7 +90,7 @@ class TaskInterfacesStorage{ | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|         GwApi::TaskInterfaces::Ptr get(const String &name, int &result){ |         GwApi::TaskInterfaces::Ptr get(const String &name, int &result){ | ||||||
|             GWSYNCHRONIZED(&lock); |             GWSYNCHRONIZED(lock); | ||||||
|             auto it = values.find(name); |             auto it = values.find(name); | ||||||
|             if (it == values.end()) |             if (it == values.end()) | ||||||
|             { |             { | ||||||
|  | @ -150,36 +99,59 @@ class TaskInterfacesStorage{ | ||||||
|             } |             } | ||||||
|             result = it->second.updates; |             result = it->second.updates; | ||||||
|             return it->second.ptr; |             return it->second.ptr; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bool update(const String &name, std::function<GwApi::TaskInterfaces::Ptr(GwApi::TaskInterfaces::Ptr)>f){ | ||||||
|  |             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{ | class TaskInterfacesImpl : public GwApi::TaskInterfaces{ | ||||||
|     String task; | 
 | ||||||
|     TaskInterfacesStorage *storage; |     TaskInterfacesStorage *storage; | ||||||
|     GwLog *logger; |     GwLog *logger; | ||||||
|     bool isInit=false; |     bool isInit=false; | ||||||
|     public: |     public: | ||||||
|         TaskInterfacesImpl(const String &n,TaskInterfacesStorage *s, GwLog *l,bool i): |         TaskInterfacesImpl(TaskInterfacesStorage *s, GwLog *l,bool i): | ||||||
|             task(n),storage(s),isInit(i),logger(l){} |             storage(s),isInit(i),logger(l){} | ||||||
|         virtual bool iset(const String &file, const String &name, Ptr v){ |     protected: | ||||||
|             return storage->set(file,name,task,v); |         virtual bool iset(const String &name, Ptr v){ | ||||||
|  |             return storage->set(name,v); | ||||||
|         } |         } | ||||||
|         virtual Ptr iget(const String &name, int &result){ |         virtual Ptr iget(const String &name, int &result){ | ||||||
|             return storage->get(name,result); |             return storage->get(name,result); | ||||||
|         } |         } | ||||||
|         virtual bool iclaim(const String &name, const String &task){ |         virtual bool iupdate(const String &name,std::function<Ptr(Ptr v)> f){ | ||||||
|             if (! isInit) return false; |             return storage->update(name,f); | ||||||
|             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; |  | ||||||
|         } |         } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -188,7 +160,7 @@ class TaskApi : public GwApiInternal | ||||||
| { | { | ||||||
|     GwApiInternal *api=nullptr; |     GwApiInternal *api=nullptr; | ||||||
|     int sourceId; |     int sourceId; | ||||||
|     SemaphoreHandle_t *mainLock; |     SemaphoreHandle_t mainLock; | ||||||
|     SemaphoreHandle_t localLock; |     SemaphoreHandle_t localLock; | ||||||
|     std::map<int,GwCounter<String>> counter; |     std::map<int,GwCounter<String>> counter; | ||||||
|     std::map<String,GwApi::HandlerFunction> webHandlers; |     std::map<String,GwApi::HandlerFunction> webHandlers; | ||||||
|  | @ -200,7 +172,7 @@ class TaskApi : public GwApiInternal | ||||||
| public: | public: | ||||||
|     TaskApi(GwApiInternal *api,  |     TaskApi(GwApiInternal *api,  | ||||||
|         int sourceId,  |         int sourceId,  | ||||||
|         SemaphoreHandle_t *mainLock,  |         SemaphoreHandle_t mainLock,  | ||||||
|         const String &name, |         const String &name, | ||||||
|         TaskInterfacesStorage *s, |         TaskInterfacesStorage *s, | ||||||
|         bool init=false) |         bool init=false) | ||||||
|  | @ -210,7 +182,7 @@ public: | ||||||
|         this->mainLock=mainLock; |         this->mainLock=mainLock; | ||||||
|         this->name=name; |         this->name=name; | ||||||
|         localLock=xSemaphoreCreateMutex(); |         localLock=xSemaphoreCreateMutex(); | ||||||
|         interfaces=new TaskInterfacesImpl(name,s,api->getLogger(),init); |         interfaces=new TaskInterfacesImpl(s,api->getLogger(),init); | ||||||
|         isInit=init; |         isInit=init; | ||||||
|     } |     } | ||||||
|     virtual GwRequestQueue *getQueue() |     virtual GwRequestQueue *getQueue() | ||||||
|  | @ -264,14 +236,14 @@ public: | ||||||
|         vSemaphoreDelete(localLock); |         vSemaphoreDelete(localLock); | ||||||
|     }; |     }; | ||||||
|     virtual void fillStatus(GwJsonDocument &status){ |     virtual void fillStatus(GwJsonDocument &status){ | ||||||
|         GWSYNCHRONIZED(&localLock); |         GWSYNCHRONIZED(localLock); | ||||||
|         if (! counterUsed) return; |         if (! counterUsed) return; | ||||||
|         for (auto it=counter.begin();it != counter.end();it++){ |         for (auto it=counter.begin();it != counter.end();it++){ | ||||||
|             it->second.toJson(status); |             it->second.toJson(status); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     virtual int getJsonSize(){ |     virtual int getJsonSize(){ | ||||||
|         GWSYNCHRONIZED(&localLock); |         GWSYNCHRONIZED(localLock); | ||||||
|         if (! counterUsed) return 0; |         if (! counterUsed) return 0; | ||||||
|         int rt=0; |         int rt=0; | ||||||
|         for (auto it=counter.begin();it != counter.end();it++){ |         for (auto it=counter.begin();it != counter.end();it++){ | ||||||
|  | @ -280,7 +252,7 @@ public: | ||||||
|         return rt; |         return rt; | ||||||
|     }; |     }; | ||||||
|     virtual void increment(int idx,const String &name,bool failed=false){ |     virtual void increment(int idx,const String &name,bool failed=false){ | ||||||
|         GWSYNCHRONIZED(&localLock); |         GWSYNCHRONIZED(localLock); | ||||||
|         counterUsed=true; |         counterUsed=true; | ||||||
|         auto it=counter.find(idx); |         auto it=counter.find(idx); | ||||||
|         if (it == counter.end()) return; |         if (it == counter.end()) return; | ||||||
|  | @ -288,18 +260,18 @@ public: | ||||||
|         else (it->second.add(name)); |         else (it->second.add(name)); | ||||||
|     }; |     }; | ||||||
|     virtual void reset(int idx){ |     virtual void reset(int idx){ | ||||||
|         GWSYNCHRONIZED(&localLock); |         GWSYNCHRONIZED(localLock); | ||||||
|         counterUsed=true; |         counterUsed=true; | ||||||
|         auto it=counter.find(idx); |         auto it=counter.find(idx); | ||||||
|         if (it == counter.end()) return; |         if (it == counter.end()) return; | ||||||
|         it->second.reset(); |         it->second.reset(); | ||||||
|     }; |     }; | ||||||
|     virtual void remove(int idx){ |     virtual void remove(int idx){ | ||||||
|         GWSYNCHRONIZED(&localLock); |         GWSYNCHRONIZED(localLock); | ||||||
|         counter.erase(idx); |         counter.erase(idx); | ||||||
|     } |     } | ||||||
|     virtual int addCounter(const String &name){ |     virtual int addCounter(const String &name){ | ||||||
|         GWSYNCHRONIZED(&localLock); |         GWSYNCHRONIZED(localLock); | ||||||
|         counterUsed=true; |         counterUsed=true; | ||||||
|         counterIdx++; |         counterIdx++; | ||||||
|         //avoid the need for an empty counter constructor
 |         //avoid the need for an empty counter constructor
 | ||||||
|  | @ -317,7 +289,7 @@ public: | ||||||
|         return api->addXdrMapping(def); |         return api->addXdrMapping(def); | ||||||
|     } |     } | ||||||
|     virtual void registerRequestHandler(const String &url,HandlerFunction handler){ |     virtual void registerRequestHandler(const String &url,HandlerFunction handler){ | ||||||
|         GWSYNCHRONIZED(&localLock); |         GWSYNCHRONIZED(localLock); | ||||||
|         webHandlers[url]=handler; |         webHandlers[url]=handler; | ||||||
|     } |     } | ||||||
|     virtual void addCapability(const String &name, const String &value){ |     virtual void addCapability(const String &name, const String &value){ | ||||||
|  | @ -344,7 +316,7 @@ public: | ||||||
|     { |     { | ||||||
|         GwApi::HandlerFunction handler; |         GwApi::HandlerFunction handler; | ||||||
|         { |         { | ||||||
|             GWSYNCHRONIZED(&localLock); |             GWSYNCHRONIZED(localLock); | ||||||
|             auto it = webHandlers.find(url); |             auto it = webHandlers.find(url); | ||||||
|             if (it == webHandlers.end()) |             if (it == webHandlers.end()) | ||||||
|             { |             { | ||||||
|  | @ -357,12 +329,25 @@ public: | ||||||
|             handler(req); |             handler(req); | ||||||
|         return true; |         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<ConfiguredSensors>( [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->logger=api->getLogger(); | ||||||
|     this->api=api; |     this->api=api; | ||||||
|     this->mainLock=mainLock; |  | ||||||
|     this->taskData=new TaskInterfacesStorage(this->logger); |     this->taskData=new TaskInterfacesStorage(this->logger); | ||||||
| } | } | ||||||
| GwUserCode::~GwUserCode(){ | GwUserCode::~GwUserCode(){ | ||||||
|  | @ -392,6 +377,9 @@ void GwUserCode::startUserTasks(int baseId){ | ||||||
|     } |     } | ||||||
| } | } | ||||||
| void GwUserCode::startInitTasks(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()); |     LOG_DEBUG(GwLog::DEBUG,"starting %d user init tasks",initTasks.size()); | ||||||
|     for (auto it=initTasks.begin();it != initTasks.end();it++){ |     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); |         LOG_DEBUG(GwLog::LOG,"starting user init task %s with id %d",it->name.c_str(),baseId); | ||||||
|  |  | ||||||
|  | @ -15,22 +15,25 @@ class GwApiInternal : public GwApi{ | ||||||
| }; | }; | ||||||
| class GwUserTask{ | class GwUserTask{ | ||||||
|     public: |     public: | ||||||
|  |         static const int DEFAULT_STACKSIZE=2000; | ||||||
|         String name; |         String name; | ||||||
|         TaskFunction_t task=NULL; |         TaskFunction_t task=NULL; | ||||||
|         GwUserTaskFunction usertask=NULL; |         GwUserTaskFunction usertask=NULL; | ||||||
|         bool isUserTask=false; |         bool isUserTask=false; | ||||||
|         GwApiInternal *api=NULL; |         GwApiInternal *api=NULL; | ||||||
|         int stackSize=2000; |         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->name=name; | ||||||
|             this->task=task; |             this->task=task; | ||||||
|             this->stackSize=stackSize; |             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->name=name; | ||||||
|             this->usertask=task; |             this->usertask=task; | ||||||
|             this->isUserTask=true; |             this->isUserTask=true; | ||||||
|             this->stackSize=stackSize; |             this->stackSize=stackSize; | ||||||
|  |             this->order=order; | ||||||
|         } |         } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -38,13 +41,14 @@ class TaskInterfacesStorage; | ||||||
| class GwUserCode{ | class GwUserCode{ | ||||||
|     GwLog *logger; |     GwLog *logger; | ||||||
|     GwApiInternal *api; |     GwApiInternal *api; | ||||||
|     SemaphoreHandle_t *mainLock; |     SemaphoreHandle_t mainLock=nullptr; | ||||||
|     TaskInterfacesStorage *taskData; |     TaskInterfacesStorage *taskData; | ||||||
|     void startAddOnTask(GwApiInternal *api,GwUserTask *task,int sourceId,String name); |     void startAddOnTask(GwApiInternal *api,GwUserTask *task,int sourceId,String name); | ||||||
|     public: |     public: | ||||||
|         ~GwUserCode(); |         ~GwUserCode(); | ||||||
|         typedef std::map<String,String> Capabilities; |         typedef std::map<String,String> Capabilities; | ||||||
|         GwUserCode(GwApiInternal *api, SemaphoreHandle_t *mainLock); |         GwUserCode(GwApiInternal *api); | ||||||
|  |         void begin(SemaphoreHandle_t mainLock){this->mainLock=mainLock;} | ||||||
|         void startUserTasks(int baseId); |         void startUserTasks(int baseId); | ||||||
|         void startInitTasks(int baseId); |         void startInitTasks(int baseId); | ||||||
|         void startAddonTask(String name,TaskFunction_t task, int id); |         void startAddonTask(String name,TaskFunction_t task, int id); | ||||||
|  |  | ||||||
|  | @ -58,6 +58,7 @@ GwXDRType *types[] = { | ||||||
|     new GwXDRType(GwXDRType::DISPLACEMENTD, "A", "D",DegToRad,RadToDeg,"rd"), |     new GwXDRType(GwXDRType::DISPLACEMENTD, "A", "D",DegToRad,RadToDeg,"rd"), | ||||||
|     new GwXDRType(GwXDRType::RPM,"T","R") |     new GwXDRType(GwXDRType::RPM,"T","R") | ||||||
|     }; |     }; | ||||||
|  | static GwXDRType genericType(GwXDRType::GENERIC, "G", ""); | ||||||
| template<typename T, int size> | template<typename T, int size> | ||||||
| int GetArrLength(T(&)[size]){return size;} | int GetArrLength(T(&)[size]){return size;} | ||||||
| static GwXDRType *findType(GwXDRType::TypeCode type, int *start = NULL) | static GwXDRType *findType(GwXDRType::TypeCode type, int *start = NULL) | ||||||
|  | @ -82,6 +83,19 @@ static GwXDRType *findType(GwXDRType::TypeCode type, int *start = NULL) | ||||||
|     return 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" | #include "GwXdrTypeMappings.h" | ||||||
| 
 | 
 | ||||||
| static GwXDRType::TypeCode findTypeMapping(GwXDRCategory category, int field) | static GwXDRType::TypeCode findTypeMapping(GwXDRCategory category, int field) | ||||||
|  | @ -199,7 +213,7 @@ GwXDRMappingDef *GwXDRMappingDef::fromString(String s) | ||||||
|     } |     } | ||||||
|     return rt; |     return rt; | ||||||
| } | } | ||||||
| String GwXDRMappingDef::getTransducerName(int instance) | String GwXDRMappingDef::getTransducerName(int instance) const | ||||||
| { | { | ||||||
|     String name = xdrName; |     String name = xdrName; | ||||||
|     if (instanceMode == GwXDRMappingDef::IS_AUTO) |     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()); |             LOG_DEBUG(GwLog::ERROR, "no type mapping for %s", def->toString().c_str()); | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|         GwXDRType *type = findType(code, &typeIndex); |         GwXDRType *type = ::findType(code, &typeIndex); | ||||||
|         if (!type) |         if (!type) | ||||||
|         { |         { | ||||||
|             LOG_DEBUG(GwLog::ERROR, "no type definition for %s", def->toString().c_str()); |             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()); |                 LOG_DEBUG(GwLog::LOG, "append mapping with n183key %s", n183key.c_str()); | ||||||
|                 it->second.push_back(mapping); |                 it->second.push_back(mapping); | ||||||
|             } |             } | ||||||
|             type = findType(code, &typeIndex); |             type = ::findType(code, &typeIndex); | ||||||
|             if (!type) |             if (!type) | ||||||
|                 break; |                 break; | ||||||
|             mapping = new GwXDRMapping(def, type); |             mapping = new GwXDRMapping(def, type); | ||||||
|  | @ -471,7 +485,7 @@ String GwXDRMappings::getXdrEntry(String mapping, double value,int instance){ | ||||||
|     { |     { | ||||||
|         return rt; |         return rt; | ||||||
|     } |     } | ||||||
|     GwXDRType *type = findType(code, &typeIndex); |     GwXDRType *type = ::findType(code, &typeIndex); | ||||||
|     bool first=true; |     bool first=true; | ||||||
|     unsigned long invalidTime=config->getInt(GwConfigDefinitions::timoSensor); |     unsigned long invalidTime=config->getInt(GwConfigDefinitions::timoSensor); | ||||||
|     while (type){ |     while (type){ | ||||||
|  | @ -480,8 +494,12 @@ String GwXDRMappings::getXdrEntry(String mapping, double value,int instance){ | ||||||
|         if (first) first=false; |         if (first) first=false; | ||||||
|         else rt+=","; |         else rt+=","; | ||||||
|         rt+=found.buildXdrEntry(value).entry; |         rt+=found.buildXdrEntry(value).entry; | ||||||
|         type = findType(code, &typeIndex); |         type = ::findType(code, &typeIndex); | ||||||
|     } |     } | ||||||
|     delete def; |     delete def; | ||||||
|     return rt; |     return rt; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | const GwXDRType * GwXDRMappings::findType(const String &typeString, const String &unitString) const{ | ||||||
|  |     return ::findType(typeString,unitString); | ||||||
|  | } | ||||||
|  | @ -140,7 +140,7 @@ class GwXDRMappingDef{ | ||||||
|         rt += xdrUnit; |         rt += xdrUnit; | ||||||
|         return rt; |         return rt; | ||||||
|     } |     } | ||||||
|     String getTransducerName(int instance); |     String getTransducerName(int instance) const; | ||||||
|     private: |     private: | ||||||
|     static bool handleToken(String tok,int index,GwXDRMappingDef *def); |     static bool handleToken(String tok,int index,GwXDRMappingDef *def); | ||||||
| }; | }; | ||||||
|  | @ -163,12 +163,12 @@ class GwXDRFoundMapping : public GwBoatItemNameProvider{ | ||||||
|             String entry; |             String entry; | ||||||
|             String transducer; |             String transducer; | ||||||
|         }; |         }; | ||||||
|         GwXDRMappingDef *definition=NULL; |         const GwXDRMappingDef *definition=NULL; | ||||||
|         GwXDRType *type=NULL; |         const GwXDRType *type=NULL; | ||||||
|         int instanceId=-1; |         int instanceId=-1; | ||||||
|         bool empty=true; |         bool empty=true; | ||||||
|         unsigned long timeout=0; |         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->definition=definition; | ||||||
|             this->type=type; |             this->type=type; | ||||||
|             this->timeout=timeout; |             this->timeout=timeout; | ||||||
|  | @ -182,7 +182,7 @@ class GwXDRFoundMapping : public GwBoatItemNameProvider{ | ||||||
|             empty=false; |             empty=false; | ||||||
|         } |         } | ||||||
|         GwXDRFoundMapping(){} |         GwXDRFoundMapping(){} | ||||||
|         String getTransducerName(){ |         virtual String getTransducerName(){ | ||||||
|             return definition->getTransducerName(instanceId); |             return definition->getTransducerName(instanceId); | ||||||
|         } |         } | ||||||
|         double valueFromXdr(double value){ |         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 class GwXDRMappings is not intended to be deleted
 | ||||||
| //the deletion will leave memory leaks!
 | //the deletion will leave memory leaks!
 | ||||||
| class GwConfigHandler; | class GwConfigHandler; | ||||||
|  | @ -229,6 +247,7 @@ class GwXDRMappings{ | ||||||
|         GwXDRFoundMapping getMapping(GwXDRCategory category,int selector,int field=0,int instance=-1); |         GwXDRFoundMapping getMapping(GwXDRCategory category,int selector,int field=0,int instance=-1); | ||||||
|         String getXdrEntry(String mapping, double value,int instance=0); |         String getXdrEntry(String mapping, double value,int instance=0); | ||||||
|         const char * getUnMapped(); |         const char * getUnMapped(); | ||||||
|  |         const GwXDRType * findType(const String &typeString, const String &unitString) const; | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								src/main.cpp
								
								
								
								
							
							
						
						
									
										15
									
								
								src/main.cpp
								
								
								
								
							|  | @ -235,17 +235,17 @@ void SendNMEA0183Message(const tNMEA0183Msg &NMEA0183Msg, int sourceId,bool conv | ||||||
| class CalibrationValues { | class CalibrationValues { | ||||||
|   using Map=std::map<String,double>; |   using Map=std::map<String,double>; | ||||||
|   Map values; |   Map values; | ||||||
|   SemaphoreHandle_t lock; |   SemaphoreHandle_t lock=nullptr; | ||||||
|   public: |   public: | ||||||
|     CalibrationValues(){ |     CalibrationValues(){ | ||||||
|       lock=xSemaphoreCreateMutex(); |       lock=xSemaphoreCreateMutex(); | ||||||
|     } |     } | ||||||
|     void set(const String &name,double value){ |     void set(const String &name,double value){ | ||||||
|       GWSYNCHRONIZED(&lock); |       GWSYNCHRONIZED(lock); | ||||||
|       values[name]=value; |       values[name]=value; | ||||||
|     } |     } | ||||||
|     bool get(const String &name, double &value){ |     bool get(const String &name, double &value){ | ||||||
|       GWSYNCHRONIZED(&lock); |       GWSYNCHRONIZED(lock); | ||||||
|       auto it=values.find(name); |       auto it=values.find(name); | ||||||
|       if (it==values.end()) return false; |       if (it==values.end()) return false; | ||||||
|       value=it->second; |       value=it->second; | ||||||
|  | @ -373,7 +373,7 @@ bool delayedRestart(){ | ||||||
|   },"reset",2000,&logger,0,NULL) == pdPASS; |   },"reset",2000,&logger,0,NULL) == pdPASS; | ||||||
| } | } | ||||||
| ApiImpl *apiImpl=new ApiImpl(MIN_USER_TASK); | ApiImpl *apiImpl=new ApiImpl(MIN_USER_TASK); | ||||||
| GwUserCode userCodeHandler(apiImpl,&mainLock); | GwUserCode userCodeHandler(apiImpl); | ||||||
| 
 | 
 | ||||||
| #define JSON_OK "{\"status\":\"OK\"}" | #define JSON_OK "{\"status\":\"OK\"}" | ||||||
| #define JSON_INVALID_PASS F("{\"status\":\"invalid password\"}") | #define JSON_INVALID_PASS F("{\"status\":\"invalid password\"}") | ||||||
|  | @ -788,6 +788,7 @@ void setup() { | ||||||
|   logger.setWriter(new DefaultLogWriter()); |   logger.setWriter(new DefaultLogWriter()); | ||||||
| #endif | #endif | ||||||
|   boatData.begin(); |   boatData.begin(); | ||||||
|  |   userCodeHandler.begin(mainLock); | ||||||
|   userCodeHandler.startInitTasks(MIN_USER_TASK); |   userCodeHandler.startInitTasks(MIN_USER_TASK); | ||||||
|   channels.preinit(); |   channels.preinit(); | ||||||
|   config.stopChanges(); |   config.stopChanges(); | ||||||
|  | @ -849,7 +850,7 @@ void setup() { | ||||||
|                               buffer[29]=0; |                               buffer[29]=0; | ||||||
|                               request->send(200,"text/plain",buffer);     |                               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()); |                               String turl=req->url().substring(USERPREFIX.length()); | ||||||
|                               logger.logDebug(GwLog::DEBUG,"user web request for %s",turl.c_str()); |                               logger.logDebug(GwLog::DEBUG,"user web request for %s",turl.c_str()); | ||||||
|                               userCodeHandler.handleWebRequest(turl,req); |                               userCodeHandler.handleWebRequest(turl,req); | ||||||
|  | @ -937,7 +938,7 @@ void setup() { | ||||||
|   logger.logDebug(GwLog::LOG,"starting addon tasks"); |   logger.logDebug(GwLog::LOG,"starting addon tasks"); | ||||||
|   logger.flush(); |   logger.flush(); | ||||||
|   { |   { | ||||||
|     GWSYNCHRONIZED(&mainLock); |     GWSYNCHRONIZED(mainLock); | ||||||
|     userCodeHandler.startUserTasks(MIN_USER_TASK); |     userCodeHandler.startUserTasks(MIN_USER_TASK); | ||||||
|   } |   } | ||||||
|   timers.addAction(HEAP_REPORT_TIME,[](){ |   timers.addAction(HEAP_REPORT_TIME,[](){ | ||||||
|  | @ -967,7 +968,7 @@ void handleSendAndRead(bool handleRead){ | ||||||
| void loopRun() { | void loopRun() { | ||||||
|   //logger.logDebug(GwLog::DEBUG,"main loop start");
 |   //logger.logDebug(GwLog::DEBUG,"main loop start");
 | ||||||
|   monitor.reset(); |   monitor.reset(); | ||||||
|   GWSYNCHRONIZED(&mainLock); |   GWSYNCHRONIZED(mainLock); | ||||||
|   logger.flush(); |   logger.flush(); | ||||||
|   monitor.setTime(1); |   monitor.setTime(1); | ||||||
|   gwWifi.loop(); |   gwWifi.loop(); | ||||||
|  |  | ||||||
|  | @ -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.", |          "description":"send out the converted data on the NMEA2000 bus\nIf set to off the converted data will still be shown at the data tab.", | ||||||
|          "category":"converter" |          "category":"converter" | ||||||
|      }, |      }, | ||||||
|  |      { | ||||||
|  |         "name":"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", |         "name":"sendRMCi", | ||||||
|         "label":"send RMC interval", |         "label":"send RMC interval", | ||||||
|  |  | ||||||
|  | @ -1578,7 +1578,7 @@ | ||||||
|                 if (isNaN(x)) return '-----'; |                 if (isNaN(x)) return '-----'; | ||||||
|                 return formatLonLatsDecimal(x, 'lat'); |                 return formatLonLatsDecimal(x, 'lat'); | ||||||
|             }, |             }, | ||||||
|             u: '°' |             u: '' | ||||||
|         }, |         }, | ||||||
|         formatLongitude: { |         formatLongitude: { | ||||||
|             f: function (v) { |             f: function (v) { | ||||||
|  | @ -1798,7 +1798,7 @@ | ||||||
|             let id = el.getAttribute('id'); |             let id = el.getAttribute('id'); | ||||||
|             if (id) { |             if (id) { | ||||||
|                 if (!names[id.replace(/^frame_/, '')]) { |                 if (!names[id.replace(/^frame_/, '')]) { | ||||||
|                     el.parentElement.remove(); |                     el.remove(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  | @ -40,6 +40,8 @@ types: | ||||||
|     type: frame |     type: frame | ||||||
|     key: m5groovei2c#grv# |     key: m5groovei2c#grv# | ||||||
|     label: "M5 I2C Groove Units" |     label: "M5 I2C Groove Units" | ||||||
|  |     target: resource | ||||||
|  |     resource: i2cbus | ||||||
|     children: |     children: | ||||||
|       - label: "M5 ENV3" |       - label: "M5 ENV3" | ||||||
|         type: checkbox |         type: checkbox | ||||||
|  | @ -51,16 +53,16 @@ types: | ||||||
|           - value: M5_ENV3#grv# |           - value: M5_ENV3#grv# | ||||||
|             key: true |             key: true | ||||||
|             resource: qmp69881#grv#1,sht3x#grv#1 |             resource: qmp69881#grv#1,sht3x#grv#1 | ||||||
|       - label: "M5 ENV4" | #      - label: "M5 ENV4" | ||||||
|         type: checkbox | #        type: checkbox | ||||||
|         key: m5env4#grv# | #        key: m5env4#grv# | ||||||
|         target: define | #        target: define | ||||||
|         url: "https://docs.m5stack.com/en/unit/ENV%E2%85%A3%20Unit" | #        url: "https://docs.m5stack.com/en/unit/ENV%E2%85%A3%20Unit" | ||||||
|         description: "M5 sensor module temperature, humidity, pressure" | #        description: "M5 sensor module temperature, humidity, pressure" | ||||||
|         values: | #        values: | ||||||
|           - value: M5_ENV4#grv# | #          - value: M5_ENV4#grv# | ||||||
|             key: true | #            key: true | ||||||
|             resource: bmp280#grv#1,sht3x#grv#1 | #            resource: bmp280#grv#1,sht3x#grv#1 | ||||||
|       - type: checkbox |       - type: checkbox | ||||||
|         label: SHT3X-1 |         label: SHT3X-1 | ||||||
|         description: "SHT30 temperature and humidity sensor 0x44" |         description: "SHT30 temperature and humidity sensor 0x44" | ||||||
|  | @ -682,7 +684,7 @@ resources: | ||||||
|   default: &esp32default |   default: &esp32default | ||||||
|     serial: 2 |     serial: 2 | ||||||
|     can: 1 |     can: 1 | ||||||
|     i2c: 1 |     i2cbus: 2 | ||||||
|     gpio: 1  |     gpio: 1  | ||||||
| 
 | 
 | ||||||
| config: | config: | ||||||
|  |  | ||||||
|  | @ -686,7 +686,7 @@ class PipelineInfo{ | ||||||
|             let allowed=allowedResources[ak]; |             let allowed=allowedResources[ak]; | ||||||
|             if (allowed === undefined) allowed=1; |             if (allowed === undefined) allowed=1; | ||||||
|             if (resList.length > allowed){ |             if (resList.length > allowed){ | ||||||
|                 errors+=" more than "+allowed+" "+k+" device(s) used"; |                 errors+=" more than "+allowed+" device(s) of type "+k+" used"; | ||||||
|             } |             } | ||||||
|              |              | ||||||
|         } |         } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Norbert Walter
						Norbert Walter