Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. Development
  3. Device library

Device library

Scheduled Pinned Locked Moved Development
2 Posts 2 Posters 2.2k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • L Offline
    L Offline
    lrtsenar
    wrote on last edited by lrtsenar
    #1

    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());
      }
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    
    1 Reply Last reply
    1
    • H Offline
      H Offline
      hek
      Admin
      wrote on last edited by
      #2

      Interesting..

      Fits into this discussion
      http://forum.mysensors.org/topic/2464/sketch-generator

      1 Reply Last reply
      0

      Hello! It looks like you're interested in this conversation, but you don't have an account yet.

      Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

      With your input, this post could be even better 💗

      Register Login
      Reply
      • Reply as topic
      Log in to reply
      • Oldest to Newest
      • Newest to Oldest
      • Most Votes


      21

      Online

      12.0k

      Users

      11.2k

      Topics

      113.4k

      Posts


      Copyright 2025 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
      • Login

      • Don't have an account? Register

      • Login or register to search.
      • First post
        Last post
      0
      • MySensors
      • OpenHardware.io
      • Categories
      • Recent
      • Tags
      • Popular