Navigation

    • Register
    • Login
    • OpenHardware.io
    • Categories
    • Recent
    • Tags
    • Popular
    1. Home
    2. lrtsenar
    • Profile
    • Following
    • Followers
    • Topics
    • Posts
    • Best
    • Groups

    lrtsenar

    @lrtsenar

    3
    Reputation
    18
    Posts
    693
    Profile views
    0
    Followers
    0
    Following
    Joined Last Online

    lrtsenar Follow

    Best posts made by lrtsenar

    • Device library

      Hi all,

      In order to create devices (relay, button, dallas temperature, and more in the future) in a simple way, I have developped objects. My library compiles fine but I have not yet tested it because i'm waiting from China my Ardino pro mini order 😞

      I have added a preprocessor definition called "MAP" in order to use or not the standard STL C++ library. It grows the code around 5% but you can decide to not use it (which is the default choice).
      Let me share my work and tell me what do you think about it.

      Device.h

      #pragma once
      
      //#define MAP
      #ifdef MAP
      #include <StandardCplusplus.h>
      #include <map>
      #endif
      // CButton
      #include <MyMessage.h>
      #include <Bounce2.h>
      // Dallas temperature
      #include <DallasTemperature.h>
      #include <OneWire.h>
      #include "ElapsedTime.h"
      
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      #define RELAY_ON 0      // GPIO value to write to turn on attached relay
      #define RELAY_OFF 1     // GPIO value to write to turn off attached relay
      
      MySensor* g_pGW;
      #define TheSensor g_pGW
      
      class CDevice;
      #ifdef MAP
      typedef std::map<uint8_t/* id */, CDevice*> MapOfDevice;
      MapOfDevice g_deviceMap;
      #else
      #define DECLARE_NBOFDEVICE(nb) CDevice* g_deviceMap[nb];
      extern CDevice* g_deviceMap[];
      #endif
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      class CDevice
      {
      public:
          CDevice(uint8_t id, uint8_t pin, int state=0)
              : m_id(id)
              , m_pin(pin)
              , m_state(state)
          {
              g_deviceMap[m_id] = this;
      #ifdef DEBUG
              Serial.print("CDevice() id="); Serial.print(m_id);
      #endif
          }
          virtual bool getState() { return m_state; }                 // generic behavior
          virtual void setState(bool bState) { m_state = bState; }    // generic behavior
          virtual void checkChange() {};                              // used in some devices
      
      protected:
          uint8_t     m_id;
          uint8_t     m_pin;
          int         m_state;
      };
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      class CRelay : public CDevice
      {
      public:
          CRelay(uint8_t id, uint8_t pin, bool bActiveAtStart, bool bPersistent)
              : CDevice(id, pin, RELAY_OFF)
              , m_bPersistent(bPersistent)
          {
              g_pGW->present(m_id, S_LIGHT);
              digitalWrite(m_pin, bActiveAtStart ? RELAY_ON : RELAY_OFF);  // force value to be written before setting pin mode
              pinMode(m_pin, OUTPUT);
              if (m_bPersistent)
              {
                  m_state = g_pGW->loadState(m_id);   // last known state from previous run
                  digitalWrite(m_pin, m_state);
              }
          }
      
          void setState(bool bOn)
          {
              m_state = bOn ? RELAY_ON : RELAY_OFF;
              digitalWrite(m_pin, m_state);
              if (m_bPersistent)
              {
                  g_pGW->saveState(m_id, m_state);
              }
      #ifdef DEBUG
              Serial.print("CRelay::set id="); Serial.print(m_id);
              Serial.print(", new status=");   Serial.println(m_state);
      #endif
          }
      
          bool getState() { return m_state ? RELAY_ON : RELAY_OFF; }
      
      protected:
          bool        m_bPersistent;
      };
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      class CButton : public CDevice, public MyMessage, public Bounce
      {
      public:
          CButton(uint8_t id, uint8_t pin, uint8_t linkedId/*relayId*/)
              : CDevice(id, pin)
              , MyMessage(linkedId, V_LIGHT)
              , m_linkedId(linkedId)
          {
              pinMode(m_pin, INPUT);
              digitalWrite(m_pin, HIGH);  // internal pull-up
              attach(m_pin);              // Bounce class
              interval(5);                // Bounce class
          };
      
          // should be called periodically in order to check if button is pressed
          virtual void checkChange()
          {
              update();                   // Bounce class
              int iVal = read();          // Bounce class
              if (iVal != m_state)
              {
                  g_pGW->send(set(g_deviceMap[m_linkedId]->getState() ? false : true), true);    // change value
                  m_state = iVal;
              }
          }
      
      protected:
          uint8_t m_linkedId; // Id with which device (button) is associated
      };
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      class CDallasT : public CDevice, public MyMessage
      {
      public:
          // period : minimum allowed time between two request of temperature
          CDallasT(uint8_t id, uint8_t pin, uint32_t period)
              : CDevice(id, pin)
              , MyMessage(id, V_TEMP)
              , m_oneWire(pin)
              , m_dallasT(NULL)
              , m_etRequest(period)
              , m_etRetrieve(0, false, false) // don't care 2nd param, object started disabled
          {
              m_dallasT.setOneWire(&m_oneWire);
              m_dallasT.setWaitForConversion(false);  // requestTemperatures() will not block current thread
              g_pGW->present(m_id, S_TEMP);
      
              int16_t conversionTime = m_dallasT.millisToWaitForConversion(m_dallasT.getResolution());
              m_etRetrieve.setDelay(conversionTime);
          }
      
          // shoud be called periodically in order to check if temperature has to be measured
          virtual void checkChange()
          {
              if (m_etRequest.isElapsed())
              {
                  m_dallasT.requestTemperatures();    // request temperature from Dallas sensor
                  m_etRetrieve.start();               // 'wait' for end of temperature sampling
                  m_etRequest.start();                // 'wait' for next request
              }
      
              if (m_etRetrieve.isElapsed())             // wait for end of conversion
              {
                  // fetch and round temperature to one decimal
                  float tp = g_pGW->getConfig().isMetric
                           ? m_dallasT.getTempCByIndex(m_id) : m_dallasT.getTempFByIndex(m_id);
                  tp = static_cast<float>(static_cast<int>(tp * 10.)) / 10.;
      
                  if ((tp != m_fTempPrev) && (tp != -127.00) && (tp != 85.00))
                  {
                      g_pGW->send(setSensor(m_id).set(tp, 1));
                      m_fTempPrev = tp;                     // Save new temperature for next compare
                  }
                  m_etRetrieve.disable(); // once temperature is retrieved, wait for next request to retrieve again
              }
          }
      
      protected:
          OneWire             m_oneWire;
          DallasTemperature   m_dallasT;
          float               m_fTempPrev;
          CElapsedTime        m_etRequest;
          CElapsedTime        m_etRetrieve;
      };
      
      

      ElapsedTime.h

      #pragma once
      
      // used to check if set time is elapsed
      // limitation : 'isElased' method MUST be called at least 1 time every 49.71 days in order to work properly ;-)
      class CElapsedTime
      {
      public:
          // bWait1stTime means ignore timems value the very 1st call to 'isElapsed'
          CElapsedTime(uint32_t timems=0, bool bWait1stTime=false, bool bEnabled=true)
              : m_delay(timems)
              , m_bEnabled(bEnabled)
          {
              start(bWait1stTime ? timems : 0);
          };
          ~CElapsedTime() {};
      
          // start to count requested 'wait' time
          void start(uint32_t timems=0)
          {
              m_tNow = millis();
              m_tEnd = m_tNow + (timems == 0) ? m_delay : timems;
              m_bWrapped = (m_tEnd < m_tNow);
              m_bEnabled = true;
          }
      
          void disable() { m_bEnabled = false; }
      
          void setDelay(uint32_t delay) { m_delay = delay; }
      
          // check if requested time is elapsed since previous call of 'start' method
          bool isElapsed()
          {
              m_tNow = millis();
              if (m_bWrapped && (m_tNow < 0x7FFFFFFF))
              {
                  m_bWrapped = false;
              }
              return m_bEnabled && !m_bWrapped && (m_tNow > m_tEnd);
          }
      
      protected:
          uint32_t m_tNow;    // current 'time'
          uint32_t m_tEnd;    // computed end of 'time'
          uint32_t m_delay;   // used to memorize value provided in constructor
          bool m_bWrapped;    // overflow management (every around 0xFFFFFFFF/1000/3600/24 = 49.71 days)
          bool m_bEnabled;    // used to know if Elpser is enabled
      };
      
      

      RelayActuatorLrt.ino

      // History Lrt
      // 2015-12-03 v1.0 code understanding, set output pin to off at start
      // 2015-12-04 v1.1 button added
      #define WBUTTON
      // 2015-12-04 v1.2 dallas temp added
      #define WDALLASTEMP
      // 2015-12-07 v1.3 relay and button class
      // 2015-12-08 v1.4 dallas temperature device
      
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      #include <MySigningNone.h>
      #include <MyTransportNRF24.h>
      #include <MyTransportRFM69.h>
      #include <MyHwATMega328.h>
      #include <MySensor.h>
      #include <SPI.h>
      #include "Devices.h"
      
      // NRFRF24L01 radio driver (set low transmit power by default)
      MyTransportNRF24 radio(RF24_CE_PIN, RF24_CS_PIN, RF24_PA_LEVEL_GW);
      //MyTransportRFM69 radio;
      // Message signing driver (none default)
      //MySigningNone signer;
      // Select AtMega328 hardware profile
      MyHwATMega328 hw;
      // Construct MySensors library
      MySensor gw(radio, hw);
      
      #ifndef MAP
      const int NBOFDEVICE=3;
      DECLARE_NBOFDEVICE(NBOFDEVICE+1);   // index 0 is not used
      #endif
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      void setup()
      {
        gw.begin(incomingMessage, AUTO/*, true*/);    // Initialize library and add callback for incoming messages
      
        gw.sendSketchInfo("Lrt Device R+B+T", "1.4"); // Send the sketch version information to the gateway and Controller
      
        TheSensor = &gw; // gives MySensor library to Device library
      
        CRelay(1, 3, false, true); // relay where id=1, pin=3, off at start, pesistent
      
      #ifdef WBUTTON
        CButton(2, 4, 1);          // button where id=2, pin=4 and associated relayid=1
      #endif
      
      #ifdef WDALLASTEMP
        CDallasT(3, 5, 60000);     // Dallas temp where id=3, pin=5 and period temperature retrieve is 1 min.
      //  CDallasT(4, 6, 60000);     // internal module temperature
      #endif
      }
      
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      void loop()
      {
        gw.process();     // Alway process incoming messages whenever possible
      
      #if defined WBUTTON || defined WDALLASTEMP
        // parse all devices in order to know if change occurs
      #ifdef MAP
        MapOfDevice::iterator it = g_deviceMap.begin();
        for(; it != g_deviceMap.end(); ++it)
        {
          it->second->checkChange();
        }
      #else
        for(int i=1; i<=NBOFDEVICE; i++)
        {
          g_deviceMap[i]->checkChange();
        }
      #endif
      #endif
      }
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      void incomingMessage(const MyMessage &message) {
      #ifdef DEBUG
        if (message.isAck())
        {
          Serial.print("This is an ack from GW");
        }
      #endif
        if (message.type == V_LIGHT)  // relay request from GW
        {
          g_deviceMap[message.sensor]->setState(message.getBool());
        }
      }
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      
      
      posted in Development
      lrtsenar
      lrtsenar
    • RE: Compilation problem under Arduino 1.8.1

      What the f...... hell ! I have found the problem :-). It comes from the 'static' keyword while declaring the Device derivated object in Devices.h !
      I'm happy but I don't know why this new compiler forbids this...

      See you for next adventures 😉

      posted in Troubleshooting
      lrtsenar
      lrtsenar
    • RE: Sketch Generator

      If you are interested, here is my beginning of work :

      http://forum.mysensors.org/topic/2537/device-library

      posted in Feature Requests
      lrtsenar
      lrtsenar

    Latest posts made by lrtsenar

    • RE: Serial Gateway connect problem ttyAMA0

      @kimot
      After folowing your link, dmesg displays :

      0_1486335983940_upload-d22dbac5-5ecd-4d50-9b44-ee70b7d64dd1

      And :
      0_1486336890405_upload-4226c005-294c-4c8d-9de7-27e26a6467bb

      Now, sems console uses only tty1 but problem persists 😞

      posted in Domoticz
      lrtsenar
      lrtsenar
    • RE: Serial Gateway connect problem ttyAMA0

      This :
      0_1486332218660_upload-dc9673dc-b693-48f4-871c-5fbca09aa933

      But I'm not using USB to serial converter, I'm only using native serial GPIO Tx/Rx pins like I said in my 1st post.

      posted in Domoticz
      lrtsenar
      lrtsenar
    • Serial Gateway connect problem ttyAMA0

      Hi all,

      Using Mysensors 2.11, latest version of Domoticz (3.58.77), Arduino pro mini 5V directly connected to RPI2 through Tx/Rx connection (having carefully made a voltage divider for 3.3 V Rx on RPI2), have compiled SerialGateway with following parameters :
      0_1486163187143_upload-a7d023e6-ec73-428a-9a00-0936a051df99

      No connection to Serial Gateway (even after waiting for hours ) :
      0_1486163349709_upload-81bdad53-aa16-4e69-9907-b07e369c64dc

      So I tried to lock the ttyAMA0 in order to see if Domoticz is really tries to connect to ttyAMA0, so have locked ttyAMA0 :
      0_1486163122930_upload-3121397d-a0a6-4c8b-a944-573e869ad493

      And Domoticz is happy with this (as after a restart of Dmz service) !
      0_1486163299460_upload-c5703d3c-8d71-4a9e-9775-39c8b9f6e452
      No error seen...

      I suspect Domoticz doesn't try to connect to ttyAMA0. I already fully disabled the serial boot ability and log on ttyAMA0 in RPI (systemctrl diasble,sudo systemctl stop getty.target, /boot/cmdline.txt does'nt contain any ttyAMA0 reference, disable serial-getty@ttyAMA0.service, ...)
      The same hardware is fully functional with my old SD Card (Rasbian/Mysensors installed 1 year ago) !

      Is anybody knows what's wrong

      posted in Domoticz
      lrtsenar
      lrtsenar
    • RE: What are these bad practices ?

      I have found my problem. Please have a look at the link in mine (1st) post 😉

      posted in Development
      lrtsenar
      lrtsenar
    • RE: Compilation problem under Arduino 1.8.1

      What the f...... hell ! I have found the problem :-). It comes from the 'static' keyword while declaring the Device derivated object in Devices.h !
      I'm happy but I don't know why this new compiler forbids this...

      See you for next adventures 😉

      posted in Troubleshooting
      lrtsenar
      lrtsenar
    • RE: What are these bad practices ?

      Hello,

      My previous post is only a review of the MySensors library, not what I do in my code.
      Anyway, thanks for the answer.
      I will continue searching for my compilation problem.
      May I ask you with which environment you compile your sketches (Linux, Arduino IDE, ... ) ?

      posted in Development
      lrtsenar
      lrtsenar
    • RE: Combining MySensors examples

      Hello,

      Sorry but I've already done the job here :
      Please have a look at "RelayActuatorLrt.ino" in order to know how to use it, it's simple.

      [https://forum.mysensors.org/topic/2537/device-library](link url)

      Is it possible to integrate it in a future release of MySensors ?

      posted in Development
      lrtsenar
      lrtsenar
    • What are these bad practices ?

      Hello,

      I tried to compile the MySensors v2.11 library with a code which was compiling and running perfectly with Mysensors v1.42 and Arduino 1.6.5 IDE.
      I saw in this new library a lot of bad practices like including "cpp" files by the following ways (#include should only include header files) :

      #include "core/MyHwESP8266.cpp"
      

      or including directly relatives path in #include (pathes should be managed by make file) :

      #include "drivers/AVR/DigitalWriteFast/digitalWriteFast.h"
      

      I saw this too (wh'y don't declare directly the relevant type like: "const uint8_t NODE_SENSOR_ID=255" because 'C' casts are from the previous century, no ?)

      #define NODE_SENSOR_ID			((uint8_t)255)
      

      I would like to point out that I am a professional C ++ developer since about 20 years.
      I post this because i've got a LOT of errors trying compiling my code in the new library. I'd already wrote a post to expose my problem but nobody is able to understand what happens and I already spent a lot of hours trying to solve the problem.

      As soon as I add a #include of custom header file, the compiler told me "multiple definition of" :

      [https://forum.mysensors.org/topic/5928/compilation-problem-under-arduino-1-8-1](link url)

      Maybe I'm wrong and sorry for the bad review but I'm so frustrated 😞
      If anyone could help...

      posted in Development
      lrtsenar
      lrtsenar
    • Compilation problem under Arduino 1.8.1

      Hello,

      I tried to compile following code (MyMaster 2.0.0 or same result with 2.1.1) with following error code. For info, it was perfectly working with MySensors 1.5 and Arduino 1.6.5 r5) :

      Linking everything together...
      "C:_myProgFiles\arduino-1.8.1\hardware\tools\avr/bin/avr-gcc" -w -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections -mmcu=atmega328p -o "C:\Users\xxxxx\AppData\Local\Temp\arduino_build_741820/GenericDevices.ino.elf" "C:\Users\xxxxx\AppData\Local\Temp\arduino_build_741820\sketch\GenericDevices.ino.cpp.o" "C:\Users\xxxxx\AppData\Local\Temp\arduino_build_741820\libraries\SPI\SPI.cpp.o" "C:\Users\xxxxx\AppData\Local\Temp\arduino_build_741820\libraries\Bounce2\Bounce2.cpp.o" "C:\Users\xxxxx\AppData\Local\Temp\arduino_build_741820\libraries\DallasTemperature\DallasTemperature.cpp.o" "C:\Users\xxxxx\AppData\Local\Temp\arduino_build_741820\libraries\MAX31850_OneWire\OneWire.cpp.o" "C:\Users\xxxxx\AppData\Local\Temp\arduino_build_741820/core\core.a" "-LC:\Users\xxxxx\AppData\Local\Temp\arduino_build_741820" -lm
      main.cpp.o (symbol from plugin): In function atexit': (.text+0x0): multiple definition of main'
      C:\Users\xxxxx\AppData\Local\Temp\arduino_build_741820\sketch\GenericDevices.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here
      collect2.exe: error: ld returned 1 exit status

      Can anyone help me please ?
      Thx

      • GenericDevices.ino
      #define MY_RADIO_NRF24
      #define MY_RF24_PA_LEVEL RF24_PA_HIGH
      #include <MySensors.h>
      #include "Devices.h"
      
      DECLARE_ID(RELAY);
      DECLARE_ID(BUTTON);
      DECLARE_ID(DALLAST);
      
      void before()
      {
        DECLARE_RELAY(RELAY           // id / name (must be unique)
                    , 3               // pin
                    , bOff            // off at start
                    , true            // reverse output (some relay are active when 0v is present on pin and inactive at 5V)
                    , true);          // persistent
        // button to change the sate of previous declared relay
        DECLARE_BUTTON(BUTTON         // id / name
                     , 4              // pin
                     , RELAY          // linked device
                     , true);         // reverse state when button pushed
        DECLARE_DALLAST(DALLAST               // id / name
                      , 5                     // pin
                      , 30000);               // retrieve temperature every ...
      }
      void setup()
      {
      }
      
      void presentation()
      {
        sendSketchInfo("Generic Device", "2.11a");   // Send the sketch version information to the gateway and Controller
      
        for(int i=1; i<=NBOFDEVICES; i++)
        {
          if (DEVICEMAP[i] != NULL) { DEVICEMAP[i]->presentation(); }
        }
      }
      
      void loop()
      {
        // parse all devices in order to know if change occurs
        for(int i=1; i<=NBOFDEVICES; i++)
        {
            if (DEVICEMAP[i] != NULL) {  DEVICEMAP[i]->checkChange();  }
        }
      }
      
      void receive(const MyMessage &msg)
      {
          if (DEVICEMAP[msg.sensor] != NULL)
          {
            DEVICEMAP[msg.sensor]->setVal(msg);  // call corresponding sensor to take into account data coming from Controller
          }
      }
      
      • Device.h
      #pragma once
      #include <Bounce2.h>
      #include <DallasTemperature.h>
      #include <OneWire.h>
      #include "ElapsedTime.h"
      
      uint8_t g_nbOfDevice = 0;
      #define NBOFDEVICES g_nbOfDevice
      #define DECLARE_ID(id) static const uint8_t id = ++g_nbOfDevice;
      #define DECLARE_RELAY(id,pin,initialVal,reverseOutput,persistent) \
          static CDOutput o##id(id,pin,#id,initialVal,reverseOutput,0,persistent)
      #define DECLARE_BUTTON(id,pin,linkedId,reverseLinkedId) \
          static CDInput o##id(id,pin,#id,linkedId,reverseLinkedId)
      #define DECLARE_DALLAST(id,pin,period) \
          static CDallasT o##id(id,pin,#id,period);
      #define NOTEWORTHY (reinterpret_cast<CDevice*>(0xDEADBEEF))
      CDevice* g_deviceMap[11] = {NOTEWORTHY};   // static map with maximum number of device declared by node is the number minus 1
      
      void initDeviceMap()
      {
          if (g_deviceMap[0] == NOTEWORTHY)
          {
              memset(g_deviceMap, 0/*NULL*/, sizeof(g_deviceMap));
          }
      };
      #define DEVICEMAP g_deviceMap
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      class CDevice
      {
      public:
          CDevice(uint8_t id, const char* sName, uint8_t pin, uint8_t val=0)
              : m_id(id)
              , m_sName(sName)
              , m_pin(pin)
              , m_val(val)
          {
      #ifdef DEBUG
              Serial.print("CDevice() name="); Serial.print(sName);
              Serial.print(" id="); Serial.println(m_id);
      #endif
              initDeviceMap();  // run only once, after all IDs has been declared with DECLARE_XXXX macro
              g_deviceMap[m_id] = this;
          }
          virtual void presentation() {}                                      // generic behavior
          virtual uint8_t  getVal() { return m_val; }                         // generic behavior
          virtual void setVal(const MyMessage &msg) { m_val = msg.getInt(); } // generic behavior
          virtual void checkChange() {};                                      // used in some devices
      
      protected:
          void save() { saveState(m_id, m_val); }
          void load() { m_val = loadState(m_id); }     // last known state from previous run
      
          uint8_t     m_id;       // device id
          const char* m_sName;    // device name
          uint8_t     m_pin;      // device associated pin
          uint8_t     m_val;      // device generic value
      };
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      const uint8_t bOn  = 1;
      const uint8_t bOff = 0;
      class CDOutput : public CDevice
      {
      public:
          CDOutput(uint8_t id, uint8_t pin, const char* sName, bool bActiveAtStart, bool bReverseOutput, uint8_t linkedId=0, bool bPersistent=false, int sensorTypName=S_LIGHT)
              : CDevice(id, sName, pin, bActiveAtStart ? bOn : bOff)
              , m_linkedId(linkedId == 0 ? id : linkedId)
              , m_bReverseOutput(bReverseOutput)
              , m_bPersistent(bPersistent)
              , m_sensorTypName(sensorTypName)
          {
              pinMode(m_pin, OUTPUT);
              if (m_bPersistent) {  load();  }    // read and set previous state
              dWrite();
          }
      
          virtual void presentation()
          {
              if (m_linkedId == m_id)  // if not linked to other device, declare to GW
              {
                  present(m_id, m_sensorTypName, m_sName);
              }
          }
      
          void setVal(const MyMessage &msg)
          {
              if (m_linkedId == m_id)    // if linked to this device
              {
                  m_val = msg.getInt();
                  dWrite();
                  if (m_bPersistent) { save(); }
      #ifdef DEBUG
                  Serial.print("CDOutput::setVal id="); Serial.print(m_id);
                  Serial.print(", new val=");   Serial.println(m_val);
      #endif
              }
              else    // linked to other device so get value from it
              {
                  checkChange();
              }
          }
      
          void dWrite() { digitalWrite(m_pin, m_bReverseOutput ? (m_val ? bOff : bOn) : (m_val ? bOn : bOff) ); }
      
          virtual void checkChange()
          {
              if (m_linkedId != m_id)    // if linked to other device
              {
                  m_val = g_deviceMap[m_linkedId]->getVal();  // get value
                  dWrite();
                  if (m_bPersistent) { save(); }
              }
          }
      
      protected:
          uint8_t m_linkedId;
          bool    m_bReverseOutput;
          bool    m_bPersistent;
          int     m_sensorTypName;
      };
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      // used to know the state of digital input (button, state of anything on digital input)
      // TODO: a button should be associated to more than 1 device: a list of id should be managed
      // TODO: should be used to manage analog input too ?
      class CDInput : public CDevice, public MyMessage, public Bounce, public CElapsedTime
      {
      public:
          CDInput(uint8_t id, uint8_t pin, const char* sName, uint8_t linkedId=0, bool bReverse=false, uint32_t delay=0)
              : CDevice(id, sName, pin)
              , MyMessage((linkedId == 0) ? id : linkedId, V_LIGHT)
              , Bounce()
              , CElapsedTime(delay)   // used to send periodically state of this input. 0 = only when change
              , m_linkedId((linkedId == 0) ? id : linkedId)
              , m_bReverse(bReverse)
          {
              pinMode(m_pin, INPUT);
              digitalWrite(m_pin, HIGH);  // internal pull-up
              attach(m_pin);              // Bounce class
              interval(5);                // Bounce class
          };
      
          virtual void presentation()
          {
              if (m_linkedId == m_id)     // if not dedicated to linked id, declare this to GW
              {
                  present(m_id, S_BINARY, m_sName);
              }
          }
      
          // should be called periodically in order to check pin state
          virtual void checkChange()
          {
              bool bSend = false;
              update();                   // Bounce class
              int iVal = read();          // Bounce class
      
              if (!m_bEnabled)            // status should not be sent periodically
              {
                  bSend = (iVal != m_val) && ((m_linkedId != m_id) && (iVal == 0));
              }
              else if (isElapsed())       // status should be sent periodically
              {                           // in this case, always (not olny when change) send state to GW
                  bSend = true;
                  start();
              }
      
              if (bSend)
              {
                  send(set(g_deviceMap[m_linkedId]->getVal() ? !m_bReverse : m_bReverse), true);
              }
              m_val = iVal;           //  memorize old value
      
          }
      
      protected:
          uint8_t      m_linkedId;  // Id with which device (button for instance) is associated
          bool         m_bReverse;  // used for button to invert state of attached relay for instance
      };
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      #define MAX_ATTACHED_DS18B20 16
      // (D)allas (T)emperature (S)tate automaton
      const uint8_t DTS_BEGIN=1;
      const uint8_t DTS_REQUEST=2;
      const uint8_t DTS_RETRIEVE=3;
      
      class CDallasT : public CDevice, public MyMessage, public CElapsedTime
      {
      public:
          // period : minimum allowed time between two request of temperature
          CDallasT(uint8_t id, uint8_t pin, const char* sName, uint32_t period)
              : CDevice(id, sName, pin)
              , MyMessage(id, V_TEMP)
              , CElapsedTime(ELAPSED_NOW) // at startup, time is elapsed
              , m_oneWire(pin)
              , m_dallasT(&m_oneWire)
              , m_state(DTS_BEGIN)  // 1st time, request should be done
              , m_requestPeriod(period)
          {
      // try       m_dallasT.setOneWire(&m_oneWire);
              m_dallasT.begin();
              m_dallasT.setWaitForConversion(false);      // requestTemperatures() will not block current thread
          }
      
          // shoud be called periodically in order to check if temperature has to be measured
          virtual void checkChange()
          {
      // TODO: ? check if this state works in constructor. If yes, DTS_BEGIN should be removed
              if (m_state == DTS_BEGIN)   // run only once (init)
              {
                  m_nbOfSensors = m_dallasT.getDeviceCount(); // Fetch the number of attached temperature sensors
                  for (int i=0; (i<m_nbOfSensors) && (i<MAX_ATTACHED_DS18B20); i++)
                  {
                      present(m_id+i, S_TEMP, "DallasT");
                  }
                  m_conversionTime = m_dallasT.millisToWaitForConversion(m_dallasT.getResolution());
                  m_state = DTS_REQUEST;
              }
              if (isElapsed())
              {
                  if (m_state == DTS_REQUEST)
                  {
                      m_dallasT.requestTemperatures();    // request temperature from Dallas sensor
                      m_state = DTS_RETRIEVE;             // next time, retrieve temperature will be done
                      start(m_conversionTime);            // 'wait' for end of temperature sampling
                  }
                  else if (m_state == DTS_RETRIEVE)
                  {
                      // Read temperatures and send them to controller
                      for (int i=0; (i<m_nbOfSensors) && (i<MAX_ATTACHED_DS18B20); i++)
                      {
                          // fetch and round temperature to one decimal
                          float tp = getConfig().isMetric
      //                      float tp = getControllerConfig().isMetric
                                   ? m_dallasT.getTempCByIndex(i) : m_dallasT.getTempFByIndex(i);
                          tp = static_cast<float>(static_cast<int>(tp * 10.)) / 10.;
      
                          if ((tp != m_fTempPrev[i]) && (tp != -127.00) && (tp != 85.00))
                          {
                              send(setSensor(m_id+i).set(tp, 1));
                              m_fTempPrev[i] = tp;   // Save new temperature for next compare
                          }
                      }
                      m_state = DTS_REQUEST;      // next time, request temperature will be asked
                      start(m_requestPeriod);     // once temperature is retrieved, wait for next request to retrieve again
                  }
              }
          }
      
      protected:
          OneWire             m_oneWire;
          DallasTemperature   m_dallasT;
          float               m_fTempPrev[MAX_ATTACHED_DS18B20];
          uint8_t             m_state;         // internal state to toggle between 1st init, request and retrieve temperature
          uint32_t            m_requestPeriod;
          uint32_t            m_conversionTime;
          int                 m_nbOfSensors;
      };
      
      
      • ElapsedTime.h
      #pragma once
      
      const uint32_t ELAPSED_NOW=1;
      
      // used to check if set time is elapsed
      class CElapsedTime
      {
      public:
          CElapsedTime(uint32_t delay=0)  // by default, this component is disabled (delay=0) because of using it in generic derivated object
          {
              m_delay = delay;
              start();
          }
          ~CElapsedTime() {};
      
          // start to count requested 'wait' time. If delay is 0, isElapsed() always returns 'false' (component disabled)
          void start(uint32_t delay=0)
          {
              m_tStart = millis();
              if (delay != 0) {  m_delay = delay;  }
              m_bEnabled = (m_delay != 0);
          }
      
          void disable() { m_bEnabled = false; }
      
          // check if requested time is elapsed since previous call of 'start' method
          bool isElapsed()
          {
              // overflow (every 49.71 days) is natively managed by unsigned var
              return m_bEnabled && ((millis() - m_tStart) > m_delay);
          }
      
      protected:
          uint32_t m_tStart;
          uint32_t m_delay;
          bool     m_bEnabled;
      };
      
      posted in Troubleshooting
      lrtsenar
      lrtsenar