Custom I2C Wire.onRequest() Library - Class & Scope Issues

Hello Experts!

I am working with 9th graders on programming. On our most recent project, I am building a back-end that puts each student project on an I2C bus which communicates with a master arduino. This master arduino communicates serially with a PC running Processing for data visualization. It all works! However, the back-end is beyond the students and I am trying to build a library for them that will allow them to access this functionality. Adapting my working code to a library is where I ran into trouble …

For reference, I’m using Nick Gammon’s I2C_Anything library modified modified for Wire.onRequest use.

The compiler throws this error:

D:\Dropbox\Programming\Arduino\libraries\ECS_I2C\ECS_I2C.cpp: In member function ‘void ECS_I2C::begin(byte)’:
D:\Dropbox\Programming\Arduino\libraries\ECS_I2C\ECS_I2C.cpp:20: error: no matching function for call to ‘TwoWire::onRequest()’
D:\Dropbox\Programming\Arduino\arduino-1.0.5\libraries\Wire/Wire.h:67: note: candidates are: void TwoWire::onRequest(void (*)())

After a lot of research, I mostly get why this is happening. It’s looking for a free function not an instance of the class. I experimented a bit with pointers but had no luck. I then tried to make the requestEvent() static.

I then get this error, which I do not understand (didn’t I already make it a static non-member function)?

ECS_I2C\ECS_I2C.cpp.o: In function ECS_I2C::begin(unsigned char)': D:\Dropbox\Programming\Arduino\libraries\ECS_I2C/ECS_I2C.cpp:20: undefined reference to ECS_I2C::requestEvent()’

Is there a better structure to achieve what I’m trying to do? If not, what are my next steps?

Library

// Written by Nick Gammon
// May 2012

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

template <typename T> unsigned int I2C_writeAnything (const T& value)
  {
    const byte * p = (const byte*) &value;
    unsigned int i;
    for (i = 0; i < sizeof value; i++)
          Wire.write(*p++);
    return i;
  }  // end of I2C_writeAnything

template <typename T> int I2C_singleWriteAnything (const T& value) {
  int size = sizeof value;
  byte vals[size];
  const byte* p = (const byte*) &value;
  unsigned int i;
  for (i = 0; i < sizeof value; i++) {
    vals[i] = *p++;
  }
  
  Wire.write(vals, size);
  return i;
}  
  
template <typename T> unsigned int I2C_readAnything(T& value)
  {
    byte * p = (byte*) &value;
    unsigned int i;
    for (i = 0; i < sizeof value; i++)
          *p++ = Wire.read();
    return i;
  }  // end of I2C_readAnything

Original Code (pre-static).
Header

#ifndef ECS_I2C_h
#define ECS_I2C_h

#include "Arduino.h"
#include <I2C_Anything.h>

class ECS_I2C
{
  public:
    ECS_I2C();
    void begin(byte _address);
	void sendScore(int newScore);
	void requestEvent();
  private:

};
#endif

CPP

#include "Arduino.h"
#include "Wire.h"
#include <I2C_Anything.h>
#include "ECS_I2C.h"

int score;
boolean newScoreAvailable;

ECS_I2C::ECS_I2C() {
}

void ECS_I2C::begin(byte _address) {
	Wire.begin(_address);          // Start I2C Bus as a Slave
	Wire.onRequest(requestEvent);
	newScoreAvailable = false;
}

void ECS_I2C::sendScore(int newScore) {
	if(newScore > 3) score = 3;
	else if(newScore < -3) score = -3;
	else score = newScore;
	newScoreAvailable = true;
}

void ECS_I2C::requestEvent() {
	if(newScoreAvailable) {
		I2C_singleWriteAnything(score);
		newScoreAvailable = false;
	}
	else I2C_singleWriteAnything(0);
}

There is another thing I’m curious about. I am also getting the error:.

D:\Dropbox\Programming\Arduino\libraries\ECS_I2C\ECS_I2C.cpp:33: error: ‘I2C_singleWriteAnything’ was not declared in this scope
D:\Dropbox\Programming\Arduino\libraries\ECS_I2C\ECS_I2C.cpp:36: error: ‘I2C_singleWriteAnything’ was not declared in this scope

It would appear the my requestEvent function isn’t able to access these library templates. I can get around this by including them directly in my libraries header file - but thats a poor solution in my mind.

Now I’m wondering why I made that library so long-winded. Wouldn’t this work?

template <typename T> unsigned int I2C_writeAnything (const T& value) 
  {
  return  Wire.write((byte *) &value, sizeof (value));
  }

Anyway, this compiles:

CPP:

#include "Arduino.h"
#include "Wire.h"
#include "ECS_I2C.h"

int score;
boolean newScoreAvailable;

ECS_I2C::ECS_I2C() {
}

void ECS_I2C::begin(byte _address) {
	Wire.begin(_address);          // Start I2C Bus as a Slave
	Wire.onRequest(requestEvent);
	newScoreAvailable = false;
}

void ECS_I2C::sendScore(int newScore) {
	if(newScore > 3) score = 3;
	else if(newScore < -3) score = -3;
	else score = newScore;
	newScoreAvailable = true;
}

void ECS_I2C::requestEvent() {
	if(newScoreAvailable) {
		I2C_writeAnything(score);
		newScoreAvailable = false;
	}
	else I2C_writeAnything(0);
}

void setup ()
  {
  }  // end of setup

void loop ()
  {
  }  // end of loop

H:

#ifndef ECS_I2C_h
#define ECS_I2C_h

#include "Arduino.h"

template <typename T> unsigned int I2C_writeAnything (const T& value) 
  {
  return  Wire.write((byte *) &value, sizeof (value));
  }  


class ECS_I2C
{
  public:
    ECS_I2C();
    void begin(byte _address);
    void sendScore(int newScore);
    static void requestEvent();
  private:

};
#endif