Skip to content

Suggest adding I2C_writeAnything to Wire library #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
nickgammon opened this issue Aug 17, 2015 · 7 comments
Open

Suggest adding I2C_writeAnything to Wire library #62

nickgammon opened this issue Aug 17, 2015 · 7 comments
Assignees

Comments

@nickgammon
Copy link

I have the following small template library on my page about I2C ( http://www.gammon.com.au/i2c ):

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

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

It has been suggested to me that I request that you include this in the standard Wire library (Wire.h). This lets you more easily send things like floats or structs via I2C. For example:

    float fnum = 42.666;
    Wire.beginTransmission (SLAVE_ADDRESS);
    I2C_writeAnything (fnum);
    Wire.endTransmission ();

Being a template function it won't add any bloat unless you actually use it.

Related request: https://github.com/arduino/Arduino/issues/3692

@stickbreaker
Copy link

A note must be added that only ONE write operation can be used inside of the onRequestEvent() callback. Since the current Wire library resets the twi_txBufferLength value inside ..\library\Wire\utility\twi.c during the onRequestEvent() callback.

uint8_t twi_transmit(const uint8_t* data, uint8_t length){
// stuff
twi_txBufferLength = length; 
// stuff
}

Which is call from ..\library\Wire.cpp

// must be called in:
// slave tx event callback
// or after beginTransmission(address)
size_t TwoWire::write(const uint8_t *data, size_t quantity)
{
  if(transmitting){
  // in master transmitter mode
    for(size_t i = 0; i < quantity; ++i){
      write(data[i]);
    }
  }else{
  // in slave send mode
    // reply to master
    twi_transmit(data, quantity);
  }
  return quantity;
}

The boolean transmitting is false while in slave mode.

Chuck.

@nickgammon
Copy link
Author

Quite right, which is why I changed I2C_writeAnything to do a single write and not byte-by-byte. But of course you need to alert the end-user to only call that (I2C_writeAnything) once in a request event. However the point of it in the first place is that it simplifies sending (and receiving) a struct.

@stickbreaker
Copy link

I like your coding, I was just recommending that the Single write restriction should be prominently marked, or the underlying code fixed. It is a sore point for me.
I have customized Wire to support repeated calls to onRequestEvent() to allow Slave mode Arduino I2C to function like 24LCxx series EEPROMs, and added bus failure timeouts. But these fixes are deemed too confusing for novice usage.
Personally, I think that a more complex function is easier to use:

enum STAGE { twi_reStart,twi_start};
void onRequestEvent(STAGE stage){// just use a single byte Wire.write() to support unlimited lengths 
switch(stage){
  twi_start : ; // slave read bare, no register address set, Start from begining
  twi_reStart : ; // slave read, continue from prior register address
  }
}

this coding will support

Wire.begin(addr);
Wire.write(z);//register address
Wire.endTransmission(false); // set register address (z)
Wire.requestFrom(addr,x,false); // read from (z) for (x) bytes
Wire.requestFrom(addr,xmore,false); // read from (z+x) for (xmore) bytes
Wire.requestFrom(addr,xevenmore,true); // read from (z+x+xmore) for (xevenmore) bytes, and end transaction
//and now that a stop has been issued on the I2C buss
Wire.requestFrom(addr,x,false); // start reading from address (0), controlled by twi_start Case of onRequestevent()
Wire.requestFrom(addr,xmore,true);  // read from (0+x) for (xmore) bytes.

To me this added complexity makes it easier for a novice to effectively uses I2C slave mode to transfer more than one value.

Chuck.

@nickgammon
Copy link
Author

or the underlying code fixed

Quite possibly however that goes outside the scope of this particular change request. :)

@brianjmurrell
Copy link

A note must be added that only ONE write operation can be used inside of the onRequestEvent()

I just discovered this. But this just further supports adding @nickgammon's template since it allows one to do:

  uint16_t temps[6];
...
  I2C_writeAnything(temps);

to very easily send 6 uint16_ts.

@brianjmurrell
Copy link

It's also worth pointing out that there are 153 "code result" matches on GitHub of use of I2C_writeAnything(). It's very popular!

@nickgammon
Copy link
Author

Some of them use the older version which does multiple writes. Oh well. :(

My code for I2C_Anything is now on GitHub:

https://github.com/nickgammon/I2C_Anything

@sandeepmistry sandeepmistry transferred this issue from arduino/Arduino Sep 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants