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;
    };
    


  • 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 😉


Log in to reply
 

Suggested Topics

  • 3
  • 2
  • 3
  • 2
  • 1
  • 2

0
Online

11.2k
Users

11.1k
Topics

112.5k
Posts