Modifying Libraries

Hi All,

I am currently modifying a library, ModbusMaster. I have installed the library on workbench via the command palette command. Some of the modifications include adding a function from a singleton class in my main src folder.

After adding my singleton function and the include statement, I am now getting redefinition errors while locally compiling (compiling locally so the cloud library does not get included instead of my modified one, took me a while to figure out what was happening there). Error:

./inc/Arduino.h:44:19: error: redefinition of 'uint16_t makeWord(uint8_t, uint8_t)'
   44 | #define word(...) makeWord(__VA_ARGS__)

Whats the correct way to go about this? Should I just bring the ModbusMaster.cpp and .h files into my main src folder? If I do that how do I fix the same issue. Preferably they would be in my main src folder, as I have a habit of cloud compiling and would not be ideal to push the wrong firmware to my devices.

Thanks,

What's the name of the function you added?

The reason the cloud compiler didn't work is that you have to remove the dependencies.ModbusMaster line from project.properties. If you leave it in, the official version will be used instead of your modified version. I usually just insert orig_ so it becomes orig_dependencies.ModbusMaster=<version> so I can remember what version I forked. See modifying public libraries for more information.

Ok so I have now moved ModbusMaster.cpp and .h into my src folder.

In Modbus.h I put the following include statement #include "pinout.h" which is the singleton that has the function that I would like to access. This is the function in pinout.cpp that I want to use in the ModbusMaster.cpp file

void pinout::rs485_toggle(bool status){
	I2C_3.digitalWrite(P6, status);
	I2C_3.digitalWrite(P7, status);
}

which is a custom version of Modbus' TXEnablePin toggle function. the following is how I am calling the function in ModbusMaster.cpp

pinout::instance().rs485_toggle(true);

With the above setup I am still getting the redefintion error,:

./inc/Arduino.h:44:19: error: redefinition of 'uint16_t makeWord(uint8_t, uint8_t)'
   44 | #define word(...) makeWord(__VA_ARGS__)
      |                   ^~~~~~~~
./inc/Arduino.h:44:19: note: in definition of macro 'word'
   44 | #define word(...) makeWord(__VA_ARGS__)
      |                   ^~~~~~~~
./inc/Arduino.h:38:17: note: 'uint16_t makeWord(uint8_t, uint8_t)' previously defined here
   38 | inline uint16_t makeWord(uint8_t h, uint8_t l) {
      |                 ^~~~~~~~

I have updated the project.properties to the below, and cleaned the project:

name=particle_device_class_based_refactor
orig_dependencies.ModbusMaster=1.2.0

Questions:

  1. Is this because the Modbus library includes application.h?
  2. Probably not related but how come the singleton templates use the following suntax for ifndef
    #ifndef __PINOUT_H whereas the imported library uses #ifndef ModbusMaster_h (lowercase and no double underscore). Is this purely a styling convention for global and library compiler usage?
  1. Is this because the Modbus library includes application.h?

Probably not. Pretty much everything includes one of application.h, Arduino.h, or Particle.h and they should not conflict with each other.

  1. Probably not related but how come the singleton templates use the following syntax

Purely stylistic.


However, I think the problem is that you have two or more .cpp files that include Arduino.h, or a file that includes Arduino.h, such as Print.h, SPI.h, Stream.h, Wire.h, WProgram.h, WString.h.

The problem is that if that makeWord is defined in the Arduino.h file as an inline function, and a macro, but I think this is causing a duplicate symbol error when you include Arduino.h from two places.

The easiest workaround is to make sure the header files above are only included in a single .cpp file. You can use #include "application.h" in all of the others.

1 Like

so I'm pretty sure my issue is coming from the PCF8574.h library, see below for PCF8574.h and expanded error message:

In file included from ./inc/WProgram.h:1,
                 from src/PCF8574.h:39,
                 from src/pinout.h:5,
                 from src/ModbusMaster.h:40,
                 from src/ModbusMaster.cpp:32:
./inc/Arduino.h:44:19: error: redefinition of 'uint16_t makeWord(uint8_t, uint8_t)'
   44 | #define word(...) makeWord(__VA_ARGS__)
/*
 * PCF8574 GPIO Port Expand
 *
 * AUTHOR:  Renzo Mischianti
 * VERSION: 2.2.2
 *
 * https://www.mischianti.org/2019/01/02/pcf8574-i2c-digital-i-o-expander-fast-easy-usage/
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Renzo Mischianti www.mischianti.org All right reserved.
 *
 * You may copy, alter and reuse this code in any way you like, but please leave
 * reference to www.mischianti.org in your comments if you redistribute this code.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef PCF8574_h
#define PCF8574_h

// #include "Wire.h"

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#define DEFAULT_SDA SDA;
#define DEFAULT_SCL SCL;

// Uncomment to enable printing out nice debug messages.
 // #define PCF8574_DEBUG

// Uncomment for low memory usage this prevent use of complex DigitalInput structure and free 7byte of memory
// #define PCF8574_LOW_MEMORY

// Uncomment for low memory usage this prevent use of complex DigitalInput structure and free 7byte of memory
// #define PCF8574_LOW_LATENCY

//#define PCF8574_SOFT_INITIALIZATION

// Select an algorithm to manage encoder progression
 #define BASIC_ENCODER_ALGORITHM
// #define MISCHIANTI_ENCODER_ALGORITHM
// #define SEQUENCE_ENCODER_ALGORITHM_REDUCED
// #define SEQUENCE_ENCODER_ALGORITHM
// #define POKI_ENCODER_ALGORITHM

// Define where debug output will be printed.
#define DEBUG_PRINTER Serial

// Setup debug printing macros.
#ifdef PCF8574_DEBUG
	#define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); }
	#define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); }
#else
	#define DEBUG_PRINT(...) {}
	#define DEBUG_PRINTLN(...) {}
#endif

#ifdef PCF8574_LOW_LATENCY
	#define READ_ELAPSED_TIME 0
#else
	#define READ_ELAPSED_TIME 10
#endif

//#define P0  	B00000001
//#define P1  	B00000010
//#define P2  	B00000100
//#define P3  	B00001000
//#define P4  	B00010000
//#define P5  	B00100000
//#define P6  	B01000000
//#define P7  	B10000000
//
#define P0  	0
#define P1  	1
#define P2  	2
#define P3  	3
#define P4  	4
#define P5  	5
#define P6  	6
#define P7  	7

#include <math.h>


class PCF8574 {
public:

	PCF8574(uint8_t address);
	PCF8574(uint8_t address, uint8_t interruptPin,  void (*interruptFunction)() );

#if !defined(__AVR) && !defined(__STM32F1__) && !defined(TEENSYDUINO)
	PCF8574(uint8_t address, uint8_t sda, uint8_t scl);
	PCF8574(uint8_t address, uint8_t sda, uint8_t scl, uint8_t interruptPin,  void (*interruptFunction)());
#endif

#ifdef ESP32
	///// changes for second i2c bus
	PCF8574(TwoWire *pWire, uint8_t address);
	PCF8574(TwoWire *pWire, uint8_t address, uint8_t sda, uint8_t scl);

	PCF8574(TwoWire *pWire, uint8_t address, uint8_t interruptPin,  void (*interruptFunction)() );
	PCF8574(TwoWire *pWire, uint8_t address, uint8_t sda, uint8_t scl, uint8_t interruptPin,  void (*interruptFunction)());
#endif

	bool begin();
	void pinMode(uint8_t pin, uint8_t mode, uint8_t output_start = HIGH);

	void encoder(uint8_t pinA, uint8_t pinB);

	void attachInterrupt();
	void detachInterrupt();

	void readBuffer(bool force = true);
	uint8_t digitalRead(uint8_t pin, bool forceReadNow = false);
	#ifndef PCF8574_LOW_MEMORY
		struct DigitalInput {
			uint8_t p0;
			uint8_t p1;
			uint8_t p2;
			uint8_t p3;
			uint8_t p4;
			uint8_t p5;
			uint8_t p6;
			uint8_t p7;
		} digitalInput;


		DigitalInput digitalReadAll(void);

		bool digitalWriteAll(PCF8574::DigitalInput digitalInput);
	#else
		byte digitalReadAll(void);
		bool digitalWriteAll(byte digitalInput);
	#endif
	bool digitalWrite(uint8_t pin, uint8_t value);

#ifdef MISCHIANTI_ENCODER_ALGORITHM
	bool readEncoderValueMischianti(uint8_t pinA, uint8_t pinB, volatile long *encoderValue, bool reverseRotation = false);
	int8_t readEncoderValueMischianti(uint8_t pinA, uint8_t pinB);
#endif
#ifdef POKI_ENCODER_ALGORITHM
	bool readEncoderValuePoki(uint8_t pinA, uint8_t pinB, volatile long *encoderValue, bool reverseRotation = false);
	int8_t readEncoderValuePoki(uint8_t pinA, uint8_t pinB);
#endif

//	bool readEncoderValueEvolved(uint8_t pinA, uint8_t pinB, volatile long *encoderValue, bool reverseRotation = false);
//	int8_t readEncoderValueEvolved(uint8_t pinA, uint8_t pinB);

#ifdef SEQUENCE_ENCODER_ALGORITHM
	bool readEncoderValueSequence(uint8_t pinA, uint8_t pinB, volatile long *encoderValue, bool reverseRotation = false);
	int8_t readEncoderValueSequence(uint8_t pinA, uint8_t pinB);
#endif
#ifdef SEQUENCE_ENCODER_ALGORITHM_REDUCED
	bool readEncoderValueSequenceReduced(uint8_t pinA, uint8_t pinB, volatile long *encoderValue, bool reverseRotation = false);
	int8_t readEncoderValueSequenceReduced(uint8_t pinA, uint8_t pinB);
#endif
#ifdef BASIC_ENCODER_ALGORITHM
	bool readEncoderValue(uint8_t pinA, uint8_t pinB, volatile long *encoderValue, bool reverseRotation = false);
	int8_t readEncoderValue(uint8_t pinA, uint8_t pinB);
#endif

	int getLatency() const {
		return latency;
	}

	void setLatency(int latency = READ_ELAPSED_TIME) {
		this->latency = latency;
	}

	uint8_t getTransmissionStatusCode() const {
		return transmissionStatus;
	}

	bool isLastTransmissionSuccess(){
		return transmissionStatus==0;
	}
private:
	uint8_t _address;

	#if !defined(DEFAULT_SDA)
	#  if defined(__STM32F1__)
	#    define DEFAULT_SDA PB7
	#  elif defined(ESP8266)
	#    define DEFAULT_SDA 4
	#  elif defined(SDA)
	#    define DEFAULT_SDA SDA
	#  else
	#    error "Error define DEFAULT_SDA, SDA not declared, if you have this error contact the mantainer"
	#  endif
	#endif
	#if !defined(DEFAULT_SCL)
	#  if defined(__STM32F1__)
	#    define DEFAULT_SCL PB6
	#  elif defined(ESP8266)
	#    define DEFAULT_SCL 5
	#  elif defined(SDA)
	#    define DEFAULT_SCL SCL
	#  else
	#    error "Error define DEFAULT_SCL, SCL not declared, if you have this error contact the mantainer"
	#  endif
	#endif

	uint8_t _sda = DEFAULT_SDA;
	uint8_t _scl = DEFAULT_SCL;

	TwoWire *_wire;

	bool _usingInterrupt = false;
	uint8_t _interruptPin = 2;
	void (*_interruptFunction)(){};

	byte writeMode 			= 	B00000000;
	byte writeModeUp		= 	B00000000;
	byte readMode 			= 	B00000000;
	byte readModePullUp 	= 	B00000000;
	byte readModePullDown 	= 	B00000000;
	byte byteBuffered 		= 	B00000000;
	byte resetInitial		= 	B00000000;
	byte initialBuffer		= 	B00000000;
	unsigned long lastReadMillis = 0;

	byte writeByteBuffered = B00000000;

	volatile byte encoderValues = B00000000;

	uint8_t prevNextCode = 0;
	uint16_t store=0;

	int latency = READ_ELAPSED_TIME;

	bool checkProgression(byte oldValA, byte newValA, byte oldValB, byte newValB, byte validProgression);

//	byte validCW = B11100001;
//	byte validCCW = B01001011;
	byte validCW = B01001011;
	byte validCCW = B11100001;

	uint8_t transmissionStatus = 0;

	void setVal(uint8_t pin, uint8_t value);
	bool digitalWriteAllBytes(byte allpins);
};

#endif

This PCF lib,both .cpp and .h files are in my src folder as are the ModbusMaster files, is used in my pinout singleton, and includes WProgram.h. However I do not see reference to any of the other includes you mentioned in any of my other .cpp files or .h files. But I think you are spot on with the problem, I just can't seem to find where a double reference might be coming from.

EDIT:
I have also tried commenting out this block and putting the Wire include back in for the PCF file

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

So it seems like the PCF8574 library and the ModbusMaster library are just not playing nice.

I have uploaded a basic repo for error reproduction. Basically what I am trying to do, probably incorrectly, is to create I2C PCF8574 instances in my pinout singleton:

PCF8574 I2C_1(0x20);
PCF8574 I2C_2(0x21);
PCF8574 I2C_3(0x22);
PCF8574 I2C_4(0x23);

Pinout has a method called rs485_toggle that I am trying to use in the ModbusMaster class.

void pinout::rs485_toggle(bool status){
	I2C_3.digitalWrite(P6, status);
	I2C_3.digitalWrite(P7, status);
}

This function just toggles my RS485 from transmit to receive.

If anyone is willing to help take a look the repo is below:

current error message is:

In file included from ./inc/Wire.h:1,
                 from src/PCF8574.h:39,
                 from src/pinout.h:4,
                 from src/ModbusMaster.h:40,
                 from src/ModbusMaster.cpp:32:
./inc/Arduino.h:44:19: error: redefinition of 'uint16_t makeWord(uint8_t, uint8_t)'
   44 | #define word(...) makeWord(__VA_ARGS__)

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.