diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index ae4e640..273f424 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -322,4 +322,11 @@ #define CFGMODE_ledBrightness GwConfigInterface::HIDDEN #endif +#ifdef GWSPI0_CLK + #define _GWSPI +#endif +#ifdef GWSPI1_CLK + #define _GWSPI +#endif + #endif diff --git a/lib/iictask/GwIicSensors.h b/lib/iictask/GwIicSensors.h index 51f787c..c3d21b4 100644 --- a/lib/iictask/GwIicSensors.h +++ b/lib/iictask/GwIicSensors.h @@ -14,8 +14,7 @@ using BusType=TwoWire; using IICSensorList=SensorList; using IICSensorBase=SensorBase; -#define CFG_GET(name,prefix) \ - cfg->getValue(name, GwConfigDefinitions::prefix ## name) + template bool addPressureXdr(GwApi *api, CFG &cfg) diff --git a/lib/sensors/GwSensor.h b/lib/sensors/GwSensor.h index 4988dd8..e33aef3 100644 --- a/lib/sensors/GwSensor.h +++ b/lib/sensors/GwSensor.h @@ -19,7 +19,6 @@ template class SensorBase{ public: - using BusType=BUS; int busId=0; int iid=99; //N2K instanceId int addr=-1; @@ -46,4 +45,8 @@ class SensorList : public std::vector*>{ } using std::vector*>::vector; }; + +#define CFG_GET(name,prefix) \ + cfg->getValue(name, GwConfigDefinitions::prefix ## name) + #endif \ No newline at end of file diff --git a/lib/spitask/GWDMS22B.cpp b/lib/spitask/GWDMS22B.cpp new file mode 100644 index 0000000..cd4fed8 --- /dev/null +++ b/lib/spitask/GWDMS22B.cpp @@ -0,0 +1,62 @@ +/* + (C) Andreas Vogel andreas@wellenvogel.de + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "GWDMS22B.h" +#define PREFIX1 "DMS22B11" +class GWDMS22B : public SSISensor{ + uint32_t zero=2047; + public: + using SSISensor::SSISensor; + virtual bool preinit(GwApi * api){ + GwLog *logger=api->getLogger(); + LOG_DEBUG(GwLog::LOG,"DMS22B configured, prefix=%s",prefix.c_str()); + api->addCapability(prefix,"true"); + return true; + } + virtual void measure(GwApi * api,BusType *bus, int counterId){ + GwLog *logger=api->getLogger(); + uint32_t value=0; + esp_err_t res=readData(value); + if (res != ESP_OK){ + LOG_DEBUG(GwLog::ERROR,"unable to measure %s: %d",prefix.c_str(),(int)res); + } + LOG_DEBUG(GwLog::LOG,"measure %s : %d",prefix.c_str(),value); + } + #define DMS22B(prefix)\ + CFG_GET(act,prefix); \ + CFG_GET(iid,prefix); \ + CFG_GET(intv,prefix); \ + CFG_GET(zero,prefix); + virtual void readConfig(GwConfigHandler *cfg){ + if (prefix == PREFIX1){ + DMS22B(DMS22B11); + busId=GWSPIHOST1; + bits=12; + clock=500000; + #ifdef GWDMS22B11_CS + cs=GWDMS22B11_CS; + #else + cs=-1; + #endif + } + //TODO: other + } +}; + +void registerDMS22B(GwApi *api,SpiSensorList &sensors){ + #ifdef GWDMS22B11 + GWDMS22B *dms=new GWDMS22B(api,PREFIX1); + sensors.add(dms); + #endif +} diff --git a/lib/spitask/GWDMS22B.h b/lib/spitask/GWDMS22B.h new file mode 100644 index 0000000..5ef9bb5 --- /dev/null +++ b/lib/spitask/GWDMS22B.h @@ -0,0 +1,22 @@ +/* + (C) Andreas Vogel andreas@wellenvogel.de + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +/* +SSI sensor DMS22B - https://www.mouser.de/datasheet/2/54/bour_s_a0011704065_1-2262614.pdf +*/ +#ifndef _GWDMS22B_H +#define _GWDMS22B_H +#include "GwSpiSensor.h" +void registerDMS22B(GwApi *api,SpiSensorList &sensors); +#endif \ No newline at end of file diff --git a/lib/spitask/GwSpiSensor.h b/lib/spitask/GwSpiSensor.h new file mode 100644 index 0000000..76f9e64 --- /dev/null +++ b/lib/spitask/GwSpiSensor.h @@ -0,0 +1,136 @@ +/* + (C) Andreas Vogel andreas@wellenvogel.de + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef _GWSPISENSOR_H +#define _GWSPISENSOR_H +#include +#include "GwSensor.h" +#include + +class SPIBus{ + spi_host_device_t hd; + bool initialized=false; + public: + SPIBus(spi_host_device_t h):hd(h){} + bool init(GwLog*logger,int mosi=-1,int miso=-1,int clck=-1){ + spi_bus_config_t buscfg = { + .mosi_io_num = mosi, + .miso_io_num = miso, + .sclk_io_num = clck, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 0 + }; + esp_err_t res=spi_bus_initialize(hd,&buscfg,0); + if (res == ESP_OK){ + LOG_DEBUG(GwLog::LOG,"initialzed SPI bus %d,mosi=%d,miso=%d,clock=%d", + (int)hd,mosi,miso,clck); + initialized=true; + } + else{ + LOG_DEBUG(GwLog::ERROR,"unable to initialize SPI bus %d,mosi=%d,miso=%d,clock=%d, error=%d", + (int)hd,mosi,miso,clck,(int)res); + } + return initialized; + } + spi_host_device_t host() const { return hd;} +}; + +using BusType=SPIBus; + +class SSIDevice{ + spi_device_handle_t spi; + spi_host_device_t host; + bool initialized=false; + int bits=12; + public: + SSIDevice(const SPIBus *bus):host(bus->host()){} + bool init(GwLog*logger,int clock,int cs=-1,int bits=12){ + this->bits=bits; + spi_device_interface_config_t devcfg = { + .command_bits = 0, + .address_bits = 0, + .dummy_bits = 0, + .mode = 2, + .duty_cycle_pos = 128, + .cs_ena_pretrans = 0, + .cs_ena_posttrans =0, + .clock_speed_hz = clock, + .input_delay_ns =0, + .spics_io_num = cs, //CS pin + .queue_size = 1 //see https://github.com/espressif/esp-idf/issues/9450 + }; + esp_err_t res=spi_bus_add_device(host,&devcfg,&spi); + if (res == ESP_OK){ + LOG_DEBUG(GwLog::LOG,"added SSI device to bus %d, cs=%d, clock=%d", + (int)host,cs,clock); + initialized=true; + } + else{ + LOG_DEBUG(GwLog::ERROR,"unable to add SSI device to bus %d, cs=%d, clock=%d, error=%d", + (int)host,cs,clock,(int) res); + } + return initialized; + } + bool isInitialized() const { return initialized;} + spi_device_handle_t & device(){return spi;} + +}; + + +class SSISensor : public SensorBase{ + std::unique_ptr device; + protected: + int bits=12; + int mask=0xffff; + int cs=-1; + int clock=0; + bool act=false; + virtual bool initSSI(GwLog*logger,const SPIBus *bus, + int clock,int cs, int bits){ + mask= (1 << bits)-1; + device.reset(new SSIDevice(bus)); + return device->init(logger,clock,cs,bits); + } + esp_err_t readData(uint32_t &res) + { + struct spi_transaction_t ta = { + .flags = SPI_TRANS_USE_RXDATA, + .cmd = 0, + .addr = 0, + .length = bits+1, + .rxlength = 0}; + esp_err_t ret = spi_device_queue_trans(device->device(), &ta, portMAX_DELAY); + if (ret != ESP_OK) return ret; + struct spi_transaction_t *rs = NULL; + ret = spi_device_get_trans_result(device->device(), &rs, portMAX_DELAY); + if (ret != ESP_OK) return ret; + if (rs == NULL) return ESP_ERR_INVALID_RESPONSE; + res=SPI_SWAP_DATA_RX(*(uint32_t*)rs->rx_data,bits+1); + res&=mask; + return ESP_OK; + } + + public: + using SensorBase::SensorBase; + virtual bool isActive(){return act;}; + virtual bool initDevice(GwApi *api,BusType *bus){ + return initSSI(api->getLogger(),bus, clock,cs,bits); + }; + +}; +using SpiSensorList=SensorList; +#define GWSPIHOST1 SPI2_HOST +#define GWSPIHOST2 SPI3_HOST +#endif \ No newline at end of file diff --git a/lib/spitask/GwSpiTask.cpp b/lib/spitask/GwSpiTask.cpp new file mode 100644 index 0000000..14e02a9 --- /dev/null +++ b/lib/spitask/GwSpiTask.cpp @@ -0,0 +1,139 @@ +/* + (C) Andreas Vogel andreas@wellenvogel.de + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "GwSpiTask.h" +#include "GwSpiSensor.h" +#include "GWDMS22B.h" +#include "GwTimer.h" + +static SPIBus bus1(GWSPIHOST1); +static SPIBus bus2(GWSPIHOST2); + +static SpiSensorList sensors; + +#ifdef GWSPI1_CLK +static const int spi1clk=GWSPI1_CLK; +#else +static const int spi1clk=-1; +#endif +#ifdef GWSPI1_MISO +static const int spi1miso=GWSPI1_MISO; +#else +static const int spi1miso=-1; +#endif +#ifdef GWSPI1_MOSI +static const int spi1mosi=GWSPI1_MOSI; +#else +static const int spi1mosi=-1; +#endif + +#ifdef GWSPI2_CLK +static const int spi2clk=GWSPI2_CLK; +#else +static const int spi2clk=-1; +#endif +#ifdef GWSPI2_MISO +static const int spi2miso=GWSPI2_MISO; +#else +static const int spi2miso=-1; +#endif +#ifdef GWSPI2_MOSI +static const int spi2mosi=GWSPI2_MOSI; +#else +static const int spi2mosi=-1; +#endif + +#define _GWSPI +void runSpiTask(GwApi *api){ + GwLog *logger=api->getLogger(); + std::map buses; + for (auto && sensor:sensors){ + int busId=sensor->busId; + auto bus=buses.find(busId); + if (bus == buses.end()){ + switch (busId) + { + case 1: + if (spi1clk < 0){ + LOG_DEBUG(GwLog::ERROR,"SPI bus 1 not configured, cannot create %s",sensor->prefix.c_str()); + } + else{ + if (bus1.init(logger,spi1miso,spi1mosi,spi1clk)){ + buses[busId]=&bus1; + } + } + break; + case 2: + if (spi2clk < 0){ + LOG_DEBUG(GwLog::ERROR,"SPI bus 2 not configured, cannot create %s",sensor->prefix.c_str()); + } + else{ + if (bus2.init(logger,spi2miso,spi2mosi,spi2clk)){ + buses[busId]=&bus2; + } + } + break; + default: + LOG_DEBUG(GwLog::ERROR,"invalid busid %d for %s",busId,sensor->prefix.c_str()); + } + } + } + GwConfigHandler *config=api->getConfig(); + bool runLoop=false; + GwIntervalRunner timers; + int counterId=api->addCounter("spisensors"); + for (auto && sensor:sensors){ + if (!sensor->isActive()) continue; + auto bus=buses.find(sensor->busId); + if (bus == buses.end()){ + continue; + } + bool rt=sensor->initDevice(api,bus->second); + if (rt){ + runLoop=true; + timers.addAction(sensor->intv,[bus,api,sensor,counterId](){ + sensor->measure(api,bus->second,counterId); + }); + } + } + if (! runLoop){ + LOG_DEBUG(GwLog::LOG,"nothing to do for SPI task, finish"); + vTaskDelete(NULL); + return; + } + while(true){ + delay(100); + timers.loop(); + } + vTaskDelete(NULL); +} + + +void initSpiTask(GwApi *api){ + GwLog *logger=api->getLogger(); + #ifndef _GWSPI + return; + #endif + registerDMS22B(api,sensors); + bool addTask=false; + for (auto && sensor:sensors){ + if (sensor->preinit(api)) addTask=true; + } + if (addTask){ + api->addUserTask(runSpiTask,"spiTask",3000); + } + else{ + LOG_DEBUG(GwLog::LOG,"no SPI sensors defined"); + } +} \ No newline at end of file diff --git a/lib/spitask/GwSpiTask.h b/lib/spitask/GwSpiTask.h new file mode 100644 index 0000000..ddcac8b --- /dev/null +++ b/lib/spitask/GwSpiTask.h @@ -0,0 +1,20 @@ +/* + (C) Andreas Vogel andreas@wellenvogel.de + This code is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef _GWSPITASK_H +#define _GWSPITASK_H +#include "GwApi.h" +void initSpiTask(GwApi *api); +DECLARE_INITFUNCTION(initSpiTask); +#endif \ No newline at end of file diff --git a/lib/spitask/config.json b/lib/spitask/config.json new file mode 100644 index 0000000..4c9d13c --- /dev/null +++ b/lib/spitask/config.json @@ -0,0 +1,92 @@ +[ + { + "type": "array", + "name": "DMS22B", + "replace": [ + { + "b": "1", + "i": "11", + "n": "11" + }, + { + "b": "1", + "i": "12", + "n": "12" + }, + { + "b": "1", + "i": "13", + "n": "13" + }, + { + "b": "2", + "i": "21", + "n": "21" + }, + { + "b": "2", + "i": "22", + "n": "22" + }, + { + "b": "2", + "i": "23", + "n": "23" + } + + + ], + "children": [ + { + "name": "DMS22B$iact", + "label": "DMS22BX$i enable", + "type": "boolean", + "default": "true", + "description": "Enable the $i. SSI DMS22B rotary encoder (bus $b)", + "category": "spisensors$b", + "capabilities": { + "DMS22B$i": "true" + } + }, + { + "name": "DMS22B$iiid", + "label": "DMS22B$i N2K iid", + "type": "number", + "default": "$n", + "description": "the N2K instance id for the $i. DMS22B Rotary Encoder ", + "category": "spisensors$b", + "min": 0, + "max": 253, + "check": "checkMinMax", + "capabilities": { + "DMS22B$i": "true" + } + }, + { + "name": "DMS22B$iintv", + "label": "DMS22B$i Interval", + "type": "number", + "default": 2, + "description": "Interval(s) to query DMS22B rotation (0.5...10)", + "category": "spisensors$b", + "min": 0.5, + "max": 10, + "check": "checkMinMax", + "capabilities": { + "DMS22B$i": "true" + } + }, + { + "name": "DMS22B$izero", + "label": "DMS22B$i Zero", + "type": "number", + "default": 2, + "description": "Zero position (0...2^bits-1)", + "category": "spisensors$b", + "capabilities": { + "DMS22B$i": "true" + } + } + ] + } +] \ No newline at end of file diff --git a/lib/spitask/platformio.ini b/lib/spitask/platformio.ini new file mode 100644 index 0000000..d0c8f21 --- /dev/null +++ b/lib/spitask/platformio.ini @@ -0,0 +1,14 @@ +[platformio] +#basically for testing purposes +[env:m5stack-atom-spidms22b] +extends = sensors +board = m5stack-atom +lib_deps = + ${env.lib_deps} + ${sensors.lib_deps} +build_flags= + -D GWSPI1_CLK=21 + -D GWSPI1_MISO=25 + -D GWDMS22B11 + -D GWDMS22B11_CS=22 + ${env.build_flags}