I2C Registry - requesting different data

Hi all,

I a nutshell I want to set up a registry on a slave arduino.

registry1 = 0x01 registry2 = 0x02 registry3 = 0x03...

The master first send the arduino address to initiate the contact, then sends the address of a registry. The arduino (slave) then reply with the respective data.

I'm using the wire library (because I know no better) and as I understand all you cannot do the above.

Using

"Wire.onRequest(requestEvent);"

only allows you to respond if the arduino is called, and NOT allow for a second received byte to point to a particular registry (or output buffer if you prefer!).

Any ideas?

Thanks in advance,

Simon

Wouldn’t you use “on.Receive()” in the slave to get the “Read My Register” command and the register ID? Then when the request handler is activated you already know which register to return.

I tried this to no avail. After a look at a few posts on google it seems that you cant use onReceive and onRequest at the same time.

The device needs to know what to expect down the I2C, so if it gets called and it has onReceive it will suck in the following data. If it has onRequest it will send out some data straight away after receiving the device address.

Because they are both interrupts activated by the same initialiser (device address) they cannot run both at once.

Im open to any ideas, thanks!

I tried this to no avail. After a look at a few posts on google it seems that you cant use onReceive and onRequest at the same time.

That's not true, you can use both.

Post your code so we can see what you have so far.

I can't post it now because I'm not finished yet, but I'm actually in the middle of writing a tutorial on how to make an I2C slave device using an Arduino. I'll post the link when I'm done but it probably won't be for a few days.

Here we go, this is what I have so far.

Basically:

Arduino constantly reads in two sets of data from the ADC and saves to two variables.
If a master calls, it saves the 2nd byte received to a flag.
This flag then determines which registry the master is requesting.

Doesn’t work lol. I’m following a similar structure to what I have done before on PICs, hence why I need registries to make everything cross compatible.

Cheers!

#include <Wire.h>

/* Power Monitor Breakout
 
  Reads in voltage and current values from an external header (0-3v3) and exports to I2C
 
 The circuit:
 * LED attached from pin 13 to ground.
 * Note: on most Arduinos, there is already an LED on the board
 that's attached to pin 13.
 
 * Custom breakout board with 5v regulator, ADC header, common IO header.
 
 created 2011
 by Simon R. Howroyd
*/

// constants won't change. Used here to 
// set pin numbers:
const int ledPin =  13;      // the number of the LED pin
const int vPin = 7;          // the number of the voltage ADC pin
const int iPin = 6;          // the number of the current ADC pin
const int sda = 4;
const int scl = 5;
const int ID = 0xE4;         // I2C address

// Variables will change:
int ledState = LOW;             // ledState used to set the LED
int regFlag = 1;
long v = 0;
long i = 0;

void setup()
{
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);      
  Wire.begin(ID);
  Wire.onReceive(receiveEvent); // register event
  Wire.onRequest(requestEvent); // register event
  analogReference(EXTERNAL);  // Use analogue reference pin (3v3)
}

void loop()
{ 
  ///////////////////////////////////////////////////////////////////////////
  // Flash an LED
  if (ledState == LOW)
    ledState = HIGH;
  else
    ledState = LOW;
  // set the LED with the ledState of the variable:
  digitalWrite(ledPin, ledState);
  ///////////////////////////////////////////////////////////////////////////
  
  ///////////////////////////////////////////////////////////////////////////
  // ADC Code
  
  v = analogRead(vPin);  // 0 to 1023
  i = analogRead(iPin);  // 0 to 1023
  ///////////////////////////////////////////////////////////////////////////
}

void receiveEvent(int howMany)
{
  // 1st byte is  the device address handled by 'Wire'
  // 0xE4
  
  // 2nd byte is registry address
  regFlag = Wire.receive(); // receive byte as a character
}

void requestEvent()
{
  switch (regFlag){
  case 0x01:              // Registry 1
  Wire.send(highByte(v));
  Wire.send(lowByte(v));
  case 0x02:              // Registry 2
  Wire.send(highByte(i));
  Wire.send(lowByte(i));
  default:
  Wire.send(0xFF);        // Error code (255)
  }
}

The first thing I see is you’re using a device address of 0xE4. I don’t think you can use that address. Arduino uses the 7 bit address as a reference and the R/W bit is added automatically. So basically you need to use an address in the range of 0x01 - 0x7F.

The other problem is you can only use one Wire.send in requestEvent. If you have several bytes to send then put them in an array and send an array.

Don't forget to put a break after each case, unless you want case 1 to send both.

void requestEvent()
{
  switch (regFlag){
  case 0x01:              // Registry 1
  Wire.send(highByte(v));
  Wire.send(lowByte(v));
  break;

  case 0x02:              // Registry 2
  Wire.send(highByte(i));
  Wire.send(lowByte(i));
  break;

  default:
  Wire.send(0xFF);        // Error code (255)
  }
}

Schoolboy error! Cheers.

I’ve changed the I2C address to 7-bit, good spot, thank you.

As for putting the buffer into an array, whenever I do this it throws up an error:

main:85: error: no matching function for call to ‘TwoWire::send(int [2], unsigned int)’

Same happens if I use an array of pointers.

#include <Wire.h>

/* Power Monitor Breakout
 
 Reads in voltage and current values from an external header (0-3v3) and exports to I2C
 
 The circuit:
 * LED attached from pin 13 to ground.
 * Note: on most Arduinos, there is already an LED on the board
 that's attached to pin 13.
 
 * Custom breakout board with 5v regulator, ADC header, common IO header.
 
 created 2011
 by Simon R. Howroyd
 */

// constants won't change. Used here to 
// set pin numbers:
const int ledPin =  13;      // the number of the LED pin
const int vPin = 7;          // the number of the voltage ADC pin
const int iPin = 6;          // the number of the current ADC pin
const int sda = 4;
const int scl = 5;
const int ID = 0x72;         // 7-bit I2C address (0xE4,0xE5)

// Variables will change:
int ledState = LOW;             // ledState used to set the LED
int regFlag = 1;
int vBuffer[2];
int iBuffer[2];
int* outputBuffer[2];
long v = 0;
long i = 0;

void setup()
{
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);      
  Wire.begin(ID);
  Wire.onReceive(receiveEvent); // register event
  Wire.onRequest(requestEvent); // register event
  analogReference(EXTERNAL);  // Use analogue reference pin (3v3)
}

void loop()
{ 
  ///////////////////////////////////////////////////////////////////////////
  // Flash an LED
  if (ledState == LOW)
    ledState = HIGH;
  else
    ledState = LOW;
  // set the LED with the ledState of the variable:
  digitalWrite(ledPin, ledState);
  ///////////////////////////////////////////////////////////////////////////

  ///////////////////////////////////////////////////////////////////////////
  // ADC Code

    v = analogRead(vPin);  // 0 to 1023
  i = analogRead(iPin);  // 0 to 1023

  vBuffer[0] = highByte(v);
  iBuffer[0] = highByte(i);
  vBuffer[1] = lowByte(v);
  iBuffer[1] = lowByte(i);
  ///////////////////////////////////////////////////////////////////////////
}

void receiveEvent(int howMany)
{
  // 1st byte is  the device address handled by 'Wire'
  // 0xE4

  // 2nd byte is registry address
  regFlag = Wire.receive(); // receive byte as a character
}

void requestEvent()
{
  switch (regFlag){
  case 0x01:              // Registry 1
    *outputBuffer[0] = vBuffer[0];
    *outputBuffer[1] = vBuffer[1];
    Wire.send(outputBuffer,sizeof(outputBuffer));
    break;
  case 0x02:              // Registry 2
    *outputBuffer[0] = iBuffer[0];
    *outputBuffer[1] = iBuffer[1];
    Wire.send(outputBuffer,sizeof(outputBuffer));
    break;
  default:
    Wire.send(0xFF);        // Error code (255)
    break;
  }
}

Shouldn't this:

*outputBuffer[0] = vBuffer[0];
*outputBuffer[1] = vBuffer[1];

just be this:

outputBuffer[0] = vBuffer[0];
outputBuffer[1] = vBuffer[1];

Actually I think I mean to put this. I’m not the best with pointers but learning! It’s not necessary for “outputBuffer” to exist really.

pOutputBuffer[0] = &vBuffer[0];
    pOutputBuffer[1] = &vBuffer[1];

Either way, I still get the same error where it doesnt like an array going to Wire.send. Here is the full error message anyway:

main.cpp: In function 'void requestEvent()':
main:90: error: no matching function for call to 'TwoWire::send(int* [2], unsigned int)'
C:\Dropbox\Control Lab Goodness\Arduino\arduino-0022\libraries\Wire/Wire.h:54: note: candidates are: void TwoWire::send(uint8_t)
C:\Dropbox\Control Lab Goodness\Arduino\arduino-0022\libraries\Wire/Wire.h:55: note:                 void TwoWire::send(uint8_t*, uint8_t)
C:\Dropbox\Control Lab Goodness\Arduino\arduino-0022\libraries\Wire/Wire.h:56: note:                 void TwoWire::send(int)
C:\Dropbox\Control Lab Goodness\Arduino\arduino-0022\libraries\Wire/Wire.h:57: note:                 void TwoWire::send(char*)
main:95: error: no matching function for call to 'TwoWire::send(int* [2], unsigned int)'
C:\Dropbox\Control Lab Goodness\Arduino\arduino-0022\libraries\Wire/Wire.h:54: note: candidates are: void TwoWire::send(uint8_t)
C:\Dropbox\Control Lab Goodness\Arduino\arduino-0022\libraries\Wire/Wire.h:55: note:                 void TwoWire::send(uint8_t*, uint8_t)
C:\Dropbox\Control Lab Goodness\Arduino\arduino-0022\libraries\Wire/Wire.h:56: note:                 void TwoWire::send(int)
C:\Dropbox\Control Lab Goodness\Arduino\arduino-0022\libraries\Wire/Wire.h:57: note:                 void TwoWire::send(char*)

FOUND THE PROBLEM!!!

I’m too used to coding C in CSS. My array was an array on int’s. In Arduino, int is 2 bytes (in CSS it’s 1 byte). I2C obviously sends 1 byte at a time. Changing the int array to a BYTE array solved the problem. It now doesnt compile for a different reason which is more confusing tho!

main.cpp.o: In function main': C:\Dropbox\Control Lab Goodness\Arduino\arduino-0022\hardware\arduino\cores\arduino/main.cpp:7: undefined reference to setup’
C:\Dropbox\Control Lab Goodness\Arduino\arduino-0022\hardware\arduino\cores\arduino/main.cpp:10: undefined reference to `loop’

Turns out the cryptic error can be solved by doing SAVEAS and using a different file name, then re-compiling. All done now! (Ref: WyoInnovation: Arduino: undefined reference to `loop')

Here is my code now. I’ve used dynamic memory allocation as another little toy for me to play with, hopefully it doesnt leak!

#include <Wire.h>

/* Power Monitor Breakout
 
 Reads in voltage and current values from an external header (0-3v3) and exports to I2C
 
 The circuit:
 * LED attached from pin 13 to ground.
 * Note: on most Arduinos, there is already an LED on the board
 that's attached to pin 13.
 
 * Custom breakout board with 5v regulator, ADC header, common IO header.
 
 created 2011
 by Simon R. Howroyd
 */

// constants won't change. Used here to 
// set pin numbers:
const int ledPin =  13;      // the number of the LED pin
const int vPin = 7;          // the number of the voltage ADC pin
const int iPin = 6;          // the number of the current ADC pin
const int sda = 4;
const int scl = 5;
const int ID = 0x72;         // 7-bit I2C address (0xE4,0xE5)

// Variables will change:
int ledState = LOW;             // ledState used to set the LED
int registryFlag = 1;
int vBuffer[2];
int iBuffer[2];
byte* pOutputBuffer;
long v = 0;
long i = 0;

void setup()
{
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);      
  Wire.begin(ID);
  Wire.onReceive(receiveEvent); // register event
  Wire.onRequest(requestEvent); // register event
  analogReference(EXTERNAL);  // Use analogue reference pin (3v3)
}

void loop()
{ 
  while(true){
    ///////////////////////////////////////////////////////////////////////////
    // Flash an LED
    if (ledState == LOW){
      ledState = HIGH;
    }
    else{
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
    ///////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////
    // ADC Code

      v = analogRead(vPin);  // 0 to 1023
    i = analogRead(iPin);  // 0 to 1023

    vBuffer[0] = highByte(v);
    iBuffer[0] = highByte(i);
    vBuffer[1] = lowByte(v);
    iBuffer[1] = lowByte(i);
    ///////////////////////////////////////////////////////////////////////////
  }
}

void receiveEvent(int howMany)
{
  // 1st byte is  the device address handled by 'Wire'
  // 0xE4

  // 2nd byte is registry address
  registryFlag = Wire.receive(); // receive byte as a character
}

void requestEvent()  // Using better dynamic memory allocation
{
  switch (registryFlag){
  case 0x01:              // Registry 1
    pOutputBuffer = (byte*)malloc(2 * sizeof (byte)); // Setup a new pointer to the data
    pOutputBuffer[0] = vBuffer[0];
    pOutputBuffer[1] = vBuffer[1];
    Wire.send(pOutputBuffer,sizeof(pOutputBuffer)); // Send the MSB & LSB down I2C
    free(pOutputBuffer);  // Release the pointer
    pOutputBuffer = NULL; // Stop the pointer being used again until reassigned using malloc
    break;
  case 0x02:              // Registry 2
    pOutputBuffer = (byte*)malloc(2 * sizeof (byte)); // Setup a new pointer to the data
    pOutputBuffer[0] = iBuffer[0];
    pOutputBuffer[1] = iBuffer[1];
    Wire.send(pOutputBuffer,sizeof(pOutputBuffer)); // Send the MSB & LSB down I2C
    free(pOutputBuffer);  // Release the pointer
    pOutputBuffer = NULL; // Stop the pointer being used again until reassigned using malloc
    break;
  case 0x03:              // Registry 3
    pOutputBuffer = (byte*)malloc(4 * sizeof (byte)); // Setup a new pointer to the data
    pOutputBuffer[0] = vBuffer[0];
    pOutputBuffer[1] = vBuffer[1];
    pOutputBuffer[2] = iBuffer[0];
    pOutputBuffer[3] = iBuffer[1];
    Wire.send(pOutputBuffer,sizeof(pOutputBuffer)); // Send the MSB & LSB down I2C
    free(pOutputBuffer);  // Release the pointer
    pOutputBuffer = NULL; // Stop the pointer being used again until reassigned using malloc
    break;
  default:
    Wire.send(0xFF);        // Error code (255)
    break;
  }
}

I tried compiling your code and it compiles fine for me.

Yep, it's all up and running now.

Thanks for all your help!!

I know you have it working but if anyone else is interested I finally finished my tutorial on using the Wire library to make an I2C slave device. You can see it here http://dsscircuits.com/articles/arduino-i2c-slave-guide.html