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