Transfering Wire() function to a library is causing watchdog resets :/

Hi!

In my sketch I was using the Wire library to realise an I2C communication between a NodeMCU (ESP8266-12) and an Arduino Nano. That worked pretty fine. But now I am trying to transfer all the communication related functions into an “Intercom” library. But as soon as I am using my library the ESP crashes with

ets Jan  8 2013,rst cause:2, boot mode:(1,6)
 ets Jan  8 2013,rst cause:4, boot mode:(1,6)
wdt reset

I don’t know what I am doing wrong.
In my sketch I am calling

Intercom i2c(0x01, 0x08, false);

0x01 is my own master address and 0x08 is the slaves address.

The library looks like:

#include "Arduino.h"
#include <Wire.h>
#include "Intercom.h"


Intercom::Intercom(int ownMasterAddress, int foreignAddress, bool iAmArduino) :
	serialOutput(false)
{
	isArduino = iAmArduino;
	foreignI2cAddress = foreignAddress;
	initTheWireCommunication(ownMasterAddress);
}

[...]

bool Intercom::initTheWireCommunication(int masterAddress)
{
    Wire.begin(masterAddress); // join i2c bus as master on address 'masterAddress'
    Wire.onReceive(receiveEvent); // register receive event
    Wire.onRequest(requestEvent); // register request event
    return true;
}

Calling the Wire.begin() inside the constructor of Intercom is enough to cause the crash. :frowning:

How can I use the Wire library within my own Library?

It's considered good practice to implement a begin() method in a custom class. The constructor might be called before the system has been fully initialised, but a call from the setup() function is guaranteed to be safe.

Use the constructor to grab parameters to class variables, use a begin() method to do the actual initialisation from setup().

Martin-X:
It's considered good practice to implement a begin() method in a custom class. The constructor might be called before the system has been fully initialised, but a call from the setup() function is guaranteed to be safe.

Use the constructor to grab parameters to class variables, use a begin() method to do the actual initialisation from setup().

Thank you!
Never read about using a setup() method inside a library (it's not part of any tutorial), but it works!! :slight_smile: :slight_smile:

My next problem is related to the Wire.onReceive() and Wire.onRequest() callbacks. Let's think about the following:

==== mysketch.ino ====
[...]
Intercom i2c(0x01, 0x08, false);
switch (i2c.requestForeignStatus())
{
    [...]
}
==== Intercom library ====
[...]
bool Intercom::initTheWireCommunication(int masterAddress)
{
    statusCode= 0;
    Wire.begin(masterAddress); 
    Wire.onReceive(receiveEvent); 
    Wire.onRequest(requestEvent); 
    return true;
}

void Intercom::receiveEvent(int howMany)
{
    [... do something ...]
    statusCode= 0x33;
}

[...]

int Intercomm::requestForeignStatus()
{  
    return statusCode;
}

Now the problem is, that receiveEvent() cannot be a normal private member function rather a static one. But if it is defined as "static void receiveEvent()" I cannot access the rest of my class members. So every member method / variable used inside my receiveEvent() must be a static method also. This entails mostly every method to be static. :confused:

Why do you want to make it static? It handles on a object parameter doesn't it? So no static.

Why do you want it to be private? You want to call it external so just make it public...

Btw, i2c is a terrible terrible terrible variable/object name :wink:

ikarisan:
Thank you!
Never read about using a setup() method inside a library )

That's because they're usually called "begin", not "setup".

It does need to be static; onReceive() takes a simple pointer, whereas a class method has the additional this parameter.

septillion:
Why do you want to make it static? It handles on a object parameter doesn’t it? So no static.
Why do you want it to be private? You want to call it external so just make it public…

I don’t want to make it static! I am forced to do so by the compiler, because of the missing this pointer.
The receiveEvent() and the requestEvent() methods are only used inside my library. So there is no need to make them public methods.

If I define it this way:

“void receiveEvent(int howMany);”

I’ll get this error message:

X:\Develop\Arduino\IDE-Workspace\libraries\ArduinoESPIntercom\Intercom.cpp: In member function 'bool Intercom::initTheWireCommunication(int)':

X:\Develop\Arduino\IDE-Workspace\libraries\ArduinoESPIntercom\Intercom.cpp:95:29: error: no matching function for call to 'TwoWire::onReceive(<unresolved overloaded function type>)'
  Wire.onReceive(receiveEvent); // register receive event
                             ^

But if I define the onReceive() as static it compiles without an error!!
But now I am unable to access all my other class members from within the onReceive() method, because it’s static.

I don’t know how to solve this problem. :frowning:

septillion:
Btw, i2c is a terrible terrible terrible variable/object name :wink:

I know. But I was to lazy to type a longer one. :smiley:

Ok, here is my library, stripped down to the absolutely necessary:

The CPP file:

#include "Arduino.h"
#include "Wire.h"
#include "Intercom.h"

Intercom::Intercom(int ownMasterAddress, int foreignAddress, bool iAmArduino) :
	isArduino(false)
{
	isArduino = iAmArduino;
	serialOutput = false;
	foreignI2cAddress = foreignAddress;
	ownI2cAddress = ownMasterAddress;
}

void Intercom::begin()
{
	initTheWireCommunication(ownI2cAddress);
}
		
void Intercom::receiveEvent(int howMany)
{
	//TBD
}

void Intercom::requestEvent()
{
	// TBD
}

bool Intercom::initTheWireCommunication(int masterAddress)
{
	Wire.begin(masterAddress); 
	Wire.onReceive(receiveEvent); 
	Wire.onRequest(requestEvent);
	return true;
}

enum status Intercom::requestForeignStatus()
{
	Wire.beginTransmission(foreignI2cAddress);
    Wire.write(GET_STATUS);
    Wire.endTransmission();
    delay(500);
	
	[...]
	
	return UNKNOWN_ERROR;
}

The header:

#ifndef _INTERCOM_h
#define _INTERCOM_h
#include "Arduino.h"
#include <stdint.h>

class Intercom
{
	public:
		Intercom(int ownMasterAddress, int foreignAddress, bool iAmArduino = false);
		
	private:
		bool serialOutput;
		bool isArduino;
		int foreignI2cAddress;
		int ownI2cAddress;
		byte i2cRequestCommand;
		
                enum status requestForeignStatus();
		void receiveEvent(int howMany);
		void requestEvent();
		bool initTheWireCommunication(int ownMasterAddress);
		void begin();
};


#endif

Thank you!

I am afraid that I have to move all my library code back to the ino file... :frowning:

Martin-X:
It does need to be static; onReceive() takes a simple pointer, whereas a class method has the additional this parameter.

It won't be possible the modify the Wire library for my needs, isn't it?

What modification would that be, exactly?

It's like asking Albert Einstein to modify the laws of physics to meet your needs.