[SOLVED] I2C requestFrom() - how to read > 32 bytes?

@RWB, yes, I ported it okay, but have not turned into a library (I must learn how to do that one day)!

You just need to make these changes:

// HR's port
#define i2c_scl_release()                           \
                    pinMode(_sclPin, INPUT);
#define i2c_sda_release()                           \
                    pinMode(_sdaPin, INPUT);

// sets SCL low and drives output
#define i2c_scl_lo()                                \
                    pinResetFast(_sclPin);          \
                    pinMode(_sclPin, OUTPUT);  



// sets SDA low and drives output
#define i2c_sda_lo()                                \
                    pinResetFast(_sdaPin);          \
                    pinMode(_sdaPin, OUTPUT);  

// Setting SCL high and to input releases pin ie floating
// HR NOTE - not implementing PULLUP as is pulled up externally
#define i2c_scl_hi()                                \
                    pinSetFast(_sclPin);            \
                    pinMode(_sclPin, INPUT);  


// Setting SDA high and to input releases pin ie floating
// HR NOTE - not implementing PULLUP as is pulled up externally
#define i2c_sda_hi()                                \
                    pinSetFast(_sdaPin);            \
                    pinMode(_sdaPin, INPUT);      

Also follow these comments:

2016-09-19 
- Removed i2c_init() from being called in SoftI2CMaster() constructor(s)
- Renamed i2c_init() to begin()

Hope this helps.

@UMD Is this the same library source you used? https://github.com/todbot/SoftI2CMaster

Just to be sure I’m working with the same library that you used to avoid problems :smiley:

Yep, I sourced Tod E. Kurt via this mention http://todbot.com/blog/

Out of interest, you might want to check out this chap who built on Todbot;'s:

Note that I just took the source and included into my app and modified from there, ie did not make it into a library.

@UMD Thanks!

I’m going to go ahead and use this library since it comes with example code.

Looks simple to use but I’ll report back as I try to get this working.

@peekay123 Can you use the same I2C pins on the Photon with this library or do you have to use 2 separate pins other than the default I2C pins?

@RWB as long as you don’t call Wire.begin () you can use the same pins. :slight_smile:

@peekay123 Great news.

Should I be able to just change my current Wire. calls to myWire. for the new library ?

And if I never call Wire.begin(); does that mean that the Wire library does not get loaded on my Photon and therefore save some memory space?

@RWB, yes and no.

1 Like

@UMD @peekay123 @ScruffR

I went ahead and used the same library @UMD used to keep things simple.

I added the SoftI2CMaster .cpp & .h files to my program and changed the lines of code as @UMD suggested.

I figure once we get this worked out we can add it to the new library system for others :smiley:

SoftI2CMaster.cpp

/*
 * SoftI2CMaster.cpp -- Multi-instance software I2C Master library
 *
 *
 * 2010-12 Tod E. Kurt, http://todbot.com/blog/
 *
 * This code takes some tricks from:
 *  http://codinglab.blogspot.com/2008/10/i2c-on-avr-using-bit-banging.html
 *
 * 2014, by Testato: update library and examples for follow Wire’s API of Arduino IDE 1.x
 *
 */

#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

#include "SoftI2CMaster.h"

#include <util/delay.h>
#include <string.h>

#define  i2cbitdelay 50

#define  I2C_ACK  1
#define  I2C_NAK  0


// HR's port
#define i2c_scl_release()                           \
                    pinMode(_sclPin, INPUT);
#define i2c_sda_release()                           \
                    pinMode(_sdaPin, INPUT);

// sets SCL low and drives output
#define i2c_scl_lo()                                \
                    pinResetFast(_sclPin);          \
                    pinMode(_sclPin, OUTPUT);



// sets SDA low and drives output
#define i2c_sda_lo()                                \
                    pinResetFast(_sdaPin);          \
                    pinMode(_sdaPin, OUTPUT);

// Setting SCL high and to input releases pin ie floating
// HR NOTE - not implementing PULLUP as is pulled up externally
#define i2c_scl_hi()                                \
                    pinSetFast(_sclPin);            \
                    pinMode(_sclPin, INPUT);


// Setting SDA high and to input releases pin ie floating
// HR NOTE - not implementing PULLUP as is pulled up externally
#define i2c_sda_hi()                                \
                    pinSetFast(_sdaPin);            \
                    pinMode(_sdaPin, INPUT);


//
// Constructor
//
SoftI2CMaster::SoftI2CMaster()
{
    // do nothing, use setPins() later
}
//
SoftI2CMaster::SoftI2CMaster(uint8_t sclPin, uint8_t sdaPin)
{
    setPins(sclPin, sdaPin, true);
    //i2c_init();  Removed via HR's sugesstion.
}

//
SoftI2CMaster::SoftI2CMaster(uint8_t sclPin, uint8_t sdaPin, uint8_t pullups)
{
    setPins(sclPin, sdaPin, pullups);
    //i2c_init();  Removed via HR's sugesstion.
}

//
// Turn Arduino pin numbers into PORTx, DDRx, and PINx
//
void SoftI2CMaster::setPins(uint8_t sclPin, uint8_t sdaPin, uint8_t pullups)
{
    uint8_t port;

    usePullups = pullups;

    _sclPin = sclPin;
    _sdaPin = sdaPin;

    _sclBitMask = digitalPinToBitMask(sclPin);
    _sdaBitMask = digitalPinToBitMask(sdaPin);

    port = digitalPinToPort(sclPin);
    _sclPortReg  = portOutputRegister(port);
    _sclDirReg   = portModeRegister(port);

    port = digitalPinToPort(sdaPin);
    _sdaPortReg  = portOutputRegister(port);
    _sdaDirReg   = portModeRegister(port);

    initialized = 255;
}

//
//
//
uint8_t SoftI2CMaster::beginTransmission(uint8_t address)
{
    i2c_start();
    uint8_t rc = i2c_write((address<<1) | 0); // clr read bit
    // The standard Wire library returns a status in endTransmission(), not beginTransmission().
    // So we will return the status here but also remember the result so we can return it in endTransmission().
    // It also allows us to disable other I2C functions until beginTransmission has been called, if we want.
    initialized = rc;
    return rc;
}

//
uint8_t SoftI2CMaster::requestFrom(uint8_t address)
{
    i2c_start();
    uint8_t rc = i2c_write((address<<1) | 1); // set read bit
    return rc;
}
//
uint8_t SoftI2CMaster::requestFrom(int address)
{
    return requestFrom( (uint8_t) address);
}
// Added for compatibility with the standard Wire library.
uint8_t SoftI2CMaster::requestFrom(int address, int quantity)
{
    return requestFrom( (uint8_t) address);

    // Ignore 'quantity', since SoftI2CMaster::requestFrom() just sets the start of read adresses,
    // so it's the same for any number of bytes.
    (void)quantity;
}
// Added for compatibility with the standard Wire library.
uint8_t SoftI2CMaster::requestFrom(uint8_t address, uint8_t quantity)
{
    return requestFrom( (uint8_t) address);

    // Ignore 'quantity', since SoftI2CMaster::requestFrom() just sets the start of read adresses,
    // so it's the same for any number of bytes.
    (void)quantity;
}

//
uint8_t SoftI2CMaster::beginTransmission(int address)
{
    return beginTransmission((uint8_t)address);
}

//
//
//
uint8_t SoftI2CMaster::endTransmission(void)
{
    i2c_stop();
    return initialized;   // Use the result of beginTransmission()
}

// must be called in:
// slave tx event callback
// or after beginTransmission(address)
uint8_t SoftI2CMaster::write(uint8_t data)
{
    return i2c_write(data);
}

// must be called in:
// slave tx event callback
// or after beginTransmission(address)
void SoftI2CMaster::write(uint8_t* data, uint8_t quantity)
{
    for(uint8_t i = 0; i < quantity; ++i){
        write(data[i]);
    }
}

// must be called in:
// slave tx event callback
// or after beginTransmission(address)
void SoftI2CMaster::write(char* data)
{
    write((uint8_t*)data, strlen(data));
}

// must be called in:
// slave tx event callback
// or after beginTransmission(address)
void SoftI2CMaster::write(int data)
{
    write((uint8_t)data);
}

//--------------------------------------------------------------------


void SoftI2CMaster::i2c_writebit( uint8_t c )
{
    if ( c > 0 ) {
        i2c_sda_hi();
    } else {
        i2c_sda_lo();
    }

    i2c_scl_hi();
    _delay_us(i2cbitdelay);

    i2c_scl_lo();
    _delay_us(i2cbitdelay);

    if ( c > 0 ) {
        i2c_sda_lo();
    }
    _delay_us(i2cbitdelay);
}

//
uint8_t SoftI2CMaster::i2c_readbit(void)
{
    i2c_sda_hi();
    i2c_scl_hi();
    _delay_us(i2cbitdelay);

    uint8_t port = digitalPinToPort(_sdaPin);
    volatile uint8_t* pinReg = portInputRegister(port);
    uint8_t c = *pinReg;  // I2C_PIN;

    i2c_scl_lo();
    _delay_us(i2cbitdelay);

    return ( c & _sdaBitMask) ? 1 : 0;
}

// Inits bitbanging port, must be called before using the functions below
//
void SoftI2CMaster::i2c_init(void)
{
    //I2C_PORT &=~ (_BV( I2C_SDA ) | _BV( I2C_SCL ));
    //*_sclPortReg &=~ (_sdaBitMask | _sclBitMask);
    i2c_sda_hi();
    i2c_scl_hi();

    _delay_us(i2cbitdelay);
}

// Send a START Condition
//
void SoftI2CMaster::i2c_start(void)
{
    // set both to high at the same time
    //I2C_DDR &=~ (_BV( I2C_SDA ) | _BV( I2C_SCL ));
    //*_sclDirReg &=~ (_sdaBitMask | _sclBitMask);
    i2c_sda_hi();
    i2c_scl_hi();

    _delay_us(i2cbitdelay);

    i2c_sda_lo();
    _delay_us(i2cbitdelay);

    i2c_scl_lo();
    _delay_us(i2cbitdelay);
}

void SoftI2CMaster::i2c_repstart(void)
{
    // set both to high at the same time (releases drive on both lines)
    //I2C_DDR &=~ (_BV( I2C_SDA ) | _BV( I2C_SCL ));
    //*_sclDirReg &=~ (_sdaBitMask | _sclBitMask);
    i2c_sda_hi();
    i2c_scl_hi();

    i2c_scl_lo();                           // force SCL low
    _delay_us(i2cbitdelay);

    i2c_sda_release();                      // release SDA
    _delay_us(i2cbitdelay);

    i2c_scl_release();                      // release SCL
    _delay_us(i2cbitdelay);

    i2c_sda_lo();                           // force SDA low
    _delay_us(i2cbitdelay);
}

// Send a STOP Condition
//
void SoftI2CMaster::i2c_stop(void)
{
    i2c_scl_hi();
    _delay_us(i2cbitdelay);

    i2c_sda_hi();
    _delay_us(i2cbitdelay);
}

// write a byte to the I2C slave device
//
uint8_t SoftI2CMaster::i2c_write( uint8_t c )
{
    for ( uint8_t i=0;i<8;i++) {
        i2c_writebit( c & 128 );
        c<<=1;
    }

    return i2c_readbit();
}

// read a byte from the I2C slave device
//
uint8_t SoftI2CMaster::i2c_read( uint8_t ack )
{
    uint8_t res = 0;

    for ( uint8_t i=0;i<8;i++) {
        res <<= 1;
        res |= i2c_readbit();
    }

    if ( ack )
        i2c_writebit( 0 );
    else
        i2c_writebit( 1 );

    _delay_us(i2cbitdelay);

    return res;
}

// FIXME: this isn't right, surely
uint8_t SoftI2CMaster::read( uint8_t ack )
{
  return i2c_read( ack );
}

//
uint8_t SoftI2CMaster::read()
{
    return i2c_read( I2C_ACK );
}

//
uint8_t SoftI2CMaster::readLast()
{
    return i2c_read( I2C_NAK );
}

SoftI2CMaster.h

/*
 * SoftI2CMaster.h -- Multi-instance software I2C Master library
 *
 * 2010-2012 Tod E. Kurt, http://todbot.com/blog/
 * 2014, by Testato: update library and examples for follow Wire’s API of Arduino IDE 1.x
 *
 */

#ifndef SoftI2CMaster_h
#define SoftI2CMaster_h

#include <inttypes.h>

#define _SOFTI2CMASTER_VERSION 13  // software version of this library


class SoftI2CMaster
{

private:
  // per object data
  uint8_t _sclPin;
  uint8_t _sdaPin;
  uint8_t _sclBitMask;
  uint8_t _sdaBitMask;
  volatile uint8_t *_sclPortReg;
  volatile uint8_t *_sdaPortReg;
  volatile uint8_t *_sclDirReg;
  volatile uint8_t *_sdaDirReg;

  uint8_t usePullups;

  // 'initialized' will be:
  //    255 on startup,
  //    0 if beginTransmission() was called and successful,
  //    any other value if there was an error during beginTransmission().
  uint8_t initialized;

  // private methods

  void i2c_writebit( uint8_t c );
  uint8_t i2c_readbit(void);
  void i2c_init(void);
  void i2c_start(void);
  void i2c_repstart(void);
  void i2c_stop(void);
  uint8_t i2c_write( uint8_t c );
  uint8_t i2c_read( uint8_t ack );

public:
  // public methods
  SoftI2CMaster();
  SoftI2CMaster(uint8_t sclPin, uint8_t sdaPin);
  SoftI2CMaster(uint8_t sclPin, uint8_t sdaPin, uint8_t usePullups);

  void setPins(uint8_t sclPin, uint8_t sdaPin, uint8_t usePullups);

  uint8_t beginTransmission(uint8_t address);
  uint8_t beginTransmission(int address);
  uint8_t endTransmission(void);
  uint8_t write(uint8_t);
  void write(uint8_t*, uint8_t);
  void write(int);
  void write(char*);
  void begin(void) {return;};
  uint8_t requestFrom(int address);
  uint8_t requestFrom(uint8_t address);
  uint8_t requestFrom(int address, int quantity);
  uint8_t requestFrom(uint8_t address, uint8_t quantity);
  uint8_t read( uint8_t ack );
  uint8_t read();
  uint8_t readLast();

};

#endif

My .ino code is below:

#include "application.h"
#include "BQ20Z45.h"            //Include BQ78350 Header File
#include "SoftI2CMaster.h"




void setup(void)
{
  // We start the serial library to output our messages.
  Serial.begin(115200);

  // Start i2c communication.
  Wire.begin();
}


void loop(void)
{

delay(4000);

}

I only get 1 error so far when I compile the code as shown below and I’m guessing I do not need that file on the Photon and it should be removed but I wanted to ask before assuming. :wink:

@UMD Can you show me the code you used in your main .ino file for setting the I2C pins properly and then calling begin for the SoftI2CMaster bus? That would keep me from guessing :blush:

WProgram.h is the predicessor of Arduino.h and hence is only required for really old Arduinos.

But you can target 0.6.1-rc.2, which has improved Arduino portability features,

@ScruffR I removed this block and that error went away:

#if (ARDUINO >= 100)
        #include <Arduino.h>
        #else
        #include <WProgram.h>
        #endif

Now I get this error:

Is this util/delay.h also something not needed on the Photon? @UMD never mentioned changing any of this so I’m asking about all this to be sure.

If I remove the util/delay.h I get tons of other compile errors relating to delays.

@UMD can you post a complete .cpp, .h, and .ino code breakdown of the port.

@RWB, have you seen my update above

So you should be able to put that block back and try again.

BTW, @UMD said

@ScruffR Just now saw it. I’m working in the latest Particle DEV. Would I need to compile in CLI to target that firmware right?

I’m sure @UMD did not do this and got it working so I think I’ll wait and see how he got this working before compiling in the CLI since I prefer to work in DEV vs the CLI when possible.

We seem to reply too quickly :wink:

I'm not aware of a new feature in Dev to target a specific version, but I don't see the problem in using Dev and CLI in parallel. You can do all the editing in Dev and only have a console open next to it to start the CLI build/flash script.
There even is an Atom extension to have a console within Dev.

When using CLI I have a batch script that does the build, puts the device in DFU Mode and flashes --usb. That's ideal when developing non-connected projects.

@ScruffR Thanks for the quick replies :+1:

I'm sure he was not using the new library system, so before I waste to much time trying to figure out what he already has fixed I'll just go to bed and hopefully wake up to his working code :smiley:

Yes, that's not a problem at all. I've just never needed to compile in CLI due to having to use the latest beta firmware yet. I do flash my Electrons in DFU mode using the CLI on the regular, though.

I would like to install that. Can you link to it please?

There are loads, but this seems to be the one used most

But you can install it just from within Dev.
File -> Settings -> Install enter atom-terminal-panel, hit Packages and Install

You may have to install some depenencies

1 Like

@RWB, here is the code as requested:

/*
 * SoftI2CMaster.cpp -- Multi-instance software I2C Master library
 * 
 * 
 * 2010-12 Tod E. Kurt, http://todbot.com/blog/
 *
 * This code takes some tricks from:
 *  http://codinglab.blogspot.com/2008/10/i2c-on-avr-using-bit-banging.html
 *
 * 2014, by Testato: update library and examples for follow Wire’s API of Arduino IDE 1.x
 *
 */
 
/* Version history
2016-09-05 Harry Ramadan (@UMD)
- Cloned Tod E. Kurt, http://todbot.com/blog/
- Ported to Particle Photon
2016-09-14 Harry Ramadan (@UMD)
- Ready for release
2016-09-19 Harry Ramadan (@UMD)
- Removed i2c_init() from being called in SoftI2CMaster() constructor
- Renamed i2c_init() to begin()

*/



#include <application.h>

#include "softi2cmaster.h"

// uS  was 50
#define  i2cbitdelay    (unsigned int)(10)

// HR's port
#define i2c_scl_release()                           \
                    pinMode(_sclPin, INPUT);
#define i2c_sda_release()                           \
                    pinMode(_sdaPin, INPUT);

// sets SCL low and drives output
#define i2c_scl_lo()                                \
                    pinResetFast(_sclPin);          \
                    pinMode(_sclPin, OUTPUT);  



// sets SDA low and drives output
#define i2c_sda_lo()                                \
                    pinResetFast(_sdaPin);          \
                    pinMode(_sdaPin, OUTPUT);  

// Setting SCL high and to input releases pin ie floating
// HR NOTE - not implementing PULLUP as is pulled up externally
#define i2c_scl_hi()                                \
                    pinSetFast(_sclPin);            \
                    pinMode(_sclPin, INPUT);  


// Setting SDA high and to input releases pin ie floating
// HR NOTE - not implementing PULLUP as is pulled up externally
#define i2c_sda_hi()                                \
                    pinSetFast(_sdaPin);            \
                    pinMode(_sdaPin, INPUT);      

//
// Constructor
//
SoftI2CMaster::SoftI2CMaster()
{
    // do nothing, use setPins() later
}

//
SoftI2CMaster::SoftI2CMaster(uint8_t sclPin, uint8_t sdaPin) 
{
    setPins(sclPin, sdaPin, true);
    // i2c_init();  // removed 2016-09-19
}



//
SoftI2CMaster::SoftI2CMaster(uint8_t sclPin, uint8_t sdaPin, uint8_t pullups)
{
    setPins(sclPin, sdaPin, pullups);
    // i2c_init();  // removed 2016-09-19
}

//
void SoftI2CMaster::setPins(uint8_t sclPin, uint8_t sdaPin, uint8_t pullups)
{
  
    usePullups = pullups;

    _sclPin = sclPin;
    _sdaPin = sdaPin;
}

//
// Inits bitbanging port, must be called before using any functions
// Nee i2c_init();
void SoftI2CMaster::begin(void) 
{
    i2c_sda_hi();
    i2c_scl_hi();
    
    delayMicroseconds(i2cbitdelay);
    
#ifdef SOFTI2CMASTER_TRIGGER
    // Triggers for logic analysis
    digitalWrite(D4, HIGH);       
    pinMode(D4, OUTPUT);          
    digitalWrite(D5, HIGH);       
    pinMode(D5, OUTPUT);          
#endif
}


//
// Sets up writes to the device at address
//  Returns the returned ACK / NAK bit
//      0 = ACK
//      1 = NAK
//
uint8_t SoftI2CMaster::beginTransmission(uint8_t address)
{
    i2c_start();
    
    return(i2c_write((address<<1) & 0xFE));      // clr read bit
    
}


//
//
//
uint8_t SoftI2CMaster::endTransmission(void)
{
    i2c_stop();
    //return ret;  // FIXME
    return 0;
}

// Sets up a read from the device at address
//  Returns the returned ACK / NAK bit
//      0 = ACK
//      1 = NAK
//
uint8_t SoftI2CMaster::requestFrom(uint8_t address)
{
    uint8_t rc;
    

    i2c_start();
    
    rc = i2c_write((address<<1) | 1);   // set read bit
    
#ifdef SOFTI2CMASTER_DEBUG    
    Serial.printf("requestFrom(%02X) i2c_write() = %02X\r\n", address, rc);
#endif
    
    return (rc); 
}


// must be called in:
// slave tx event callback
// or after beginTransmission(address)
uint8_t SoftI2CMaster::write(uint8_t data)
{
    return i2c_write(data);
}

// must be called in:
// slave tx event callback
// or after beginTransmission(address)
void SoftI2CMaster::write(uint8_t* data, uint8_t quantity)
{
    for(uint8_t i = 0; i < quantity; ++i)
    {
        write(data[i]);
    }
}

// must be called in:
// slave tx event callback
// or after beginTransmission(address)
void SoftI2CMaster::write(char* data)
{
    write((uint8_t*)data, strlen(data));
}

//--------------------------------------------------------------------

//
// Write a byte to the I2C slave device
//  Returns the returned ACK / NAK bit
//      0 = ACK
//      1 = NAK
//
uint8_t SoftI2CMaster::i2c_write( uint8_t c )
{
    for ( uint8_t i=0; i<8; i++) 
    {
        i2c_writebit( c & 128 );
        c<<=1;
    }

    return i2c_readbit();
}

void SoftI2CMaster::i2c_writebit( uint8_t c )
{

    if ( c > 0 ) 
    {
        i2c_sda_hi();
#ifdef SOFTI2CMASTER_DEBUG 
        if (pinReadFast(_sdaPin) == 0)
        {
            // Is LOW - should NOT be - Trap it for reference - this is a big problem...
        }
#endif
        
    } 
    else 
        i2c_sda_lo();

    i2c_scl_hi();
    delayMicroseconds(i2cbitdelay);

    i2c_scl_lo();
    delayMicroseconds(i2cbitdelay);

    // finish with SDA LOW
    if ( c > 0 ) 
        i2c_sda_lo();
        
    delayMicroseconds(i2cbitdelay);
}


// FIXME: this isn't right, surely
uint8_t SoftI2CMaster::read( uint8_t ack )
{
    return i2c_read( ack );
}

//
uint8_t SoftI2CMaster::read()
{
    return i2c_read( I2C_ACK );
}

//
uint8_t SoftI2CMaster::readLast()
{
    return i2c_read( I2C_NAK );
}

//
// read a byte from the I2C slave device
//
uint8_t SoftI2CMaster::i2c_read( uint8_t ack )
{
    uint8_t res = 0;

    for ( uint8_t i=0; i<8; i++) {
        res <<= 1;
        res |= i2c_readbit();  
    }

    i2c_writebit(ack);                  // 0 = ACK - ie pull down data line
                                        // 1 = NAK

    delayMicroseconds(i2cbitdelay);

    return res;
}

//
//
uint8_t SoftI2CMaster::i2c_readbit(void)
{
#ifdef SOFTI2CMASTER_TRIGGER
    // TRIGGER for Logic Analyser
    digitalWrite(D4, LOW);
#endif   

    i2c_sda_hi();
    i2c_scl_hi();
    delayMicroseconds(i2cbitdelay);
    
    if (pinReadFast(_sclPin) == 0)
    {
        // Capture of issue! EXPERIMENT! XXXXXX
#ifdef SOFTI2CMASTER_TRIGGER
        // TRIGGERS for Logic Analyser
        digitalWrite(D5, LOW);
#endif 
        i2c_scl_hi();
    }
    
    uint8_t c = pinReadFast(_sdaPin);
    //uint8_t c = digitalRead(_sdaPin);

    i2c_scl_lo();
    delayMicroseconds(i2cbitdelay);

#ifdef SOFTI2CMASTER_TRIGGER
    // TRIGGERS for Logic Analyser
    digitalWrite(D4, HIGH);
    digitalWrite(D5, HIGH);
#endif 
    return(c);
}



// -----------------------------------------------

// Send a START Condition
//
void SoftI2CMaster::i2c_start(void)
{
    // Setting both to high releases drive on both lines
    i2c_sda_hi();
    // delayMicroseconds(i2cbitdelay);      // Looks to not be necessary
    i2c_scl_hi();

    delayMicroseconds(i2cbitdelay);
   
    i2c_sda_lo();
    delayMicroseconds(i2cbitdelay);

    i2c_scl_lo();
    delayMicroseconds(i2cbitdelay);
}

void SoftI2CMaster::i2c_repstart(void)
{
    // Setting both to high releases drive on both lines
    i2c_sda_hi();
    i2c_scl_hi();

    i2c_scl_lo();                           
    delayMicroseconds(i2cbitdelay);

    i2c_sda_release();                      
    delayMicroseconds(i2cbitdelay);

    i2c_scl_release();                      
    delayMicroseconds(i2cbitdelay);

    i2c_sda_lo();                           
    delayMicroseconds(i2cbitdelay);
}

// Send a STOP Condition
//
void SoftI2CMaster::i2c_stop(void)
{
    i2c_sda_lo();
    delayMicroseconds(i2cbitdelay);

    i2c_scl_hi();
    delayMicroseconds(i2cbitdelay);

    i2c_sda_hi();
    delayMicroseconds(i2cbitdelay);
}

Here is the header:

/*
 * SoftI2CMaster.h -- Multi-instance software I2C Master library
 * 
 * 2010-2012 Tod E. Kurt, http://todbot.com/blog/
 * 2014, by Testato: update library and examples for follow Wire’s API of Arduino IDE 1.x
 *
 */

#ifndef SoftI2CMaster_h
#define SoftI2CMaster_h

#include <inttypes.h>

#define _SOFTI2CMASTER_VERSION 14  // software version of this library

#undef  SOFTI2CMASTER_DEBUG
#undef SOFTI2CMASTER_TRIGGER

// ie pull down data line
#define  I2C_ACK  0
// ie let it float high
#define  I2C_NAK  1

class SoftI2CMaster
{

private:
  // per object data
  uint8_t _sclPin;
  uint8_t _sdaPin;
  uint8_t _sclBitMask;
  uint8_t _sdaBitMask;
  volatile uint8_t *_sclPortReg;
  volatile uint8_t *_sdaPortReg;
  volatile uint8_t *_sclDirReg;
  volatile uint8_t *_sdaDirReg;

  uint8_t usePullups;
  
  
  void i2c_writebit( uint8_t c );
  uint8_t i2c_readbit(void);
  void i2c_start(void);
  void i2c_repstart(void);
  void i2c_stop(void);
  uint8_t i2c_write( uint8_t c );
  uint8_t i2c_read( uint8_t ack );
  
public:
  
  SoftI2CMaster();
  SoftI2CMaster(uint8_t sclPin, uint8_t sdaPin);
  SoftI2CMaster(uint8_t sclPin, uint8_t sdaPin, uint8_t usePullups);

  void begin(void);

  uint8_t requestFrom(uint8_t address);

  void setPins(uint8_t sclPin, uint8_t sdaPin, uint8_t usePullups);

  uint8_t beginTransmission(uint8_t address);
  uint8_t endTransmission(void);
  
  uint8_t write(uint8_t);
  void write(uint8_t*, uint8_t);
  void write(char*);
  uint8_t requestFrom(int address);
  
  uint8_t read( uint8_t ack );
  uint8_t read();
  uint8_t readLast();

};

#endif

I note that in your code you had this in setup:

  // Start i2c communication.
  Wire.begin();

This is wrong if you want to use SoftI2CMaster!

I did things the following way so that you can revert to and from Wire and SoftI2CMaster by simply including the SoftI2CMaster.h or not.

// Comment out if you want to use the standard Wire library
#include "softi2cmaster.h"

#ifdef SoftI2CMaster_h
SoftI2CMaster  i2c = SoftI2CMaster(D1 /* SCL pin */, D0 /* SDA pin */, 0 /* pullups */);
#endif
setup()
{
#ifdef SoftI2CMaster_h
      i2c.begin();
#else
      Wire.begin();
#endif
}

Hope this helps.

2 Likes

@UMD, just a minor tip

instead of

SoftI2CMaster  i2c = SoftI2CMaster(D1 /* SCL pin */, D0 /* SDA pin */, 0 /* pullups */);

you should do

SoftI2CMaster  i2c(D1 /* SCL pin */, D0 /* SDA pin */, 0 /* pullups */);

This way you directly instantiate and use one and only one object.
The other way you’d first instantiate a “dummy” object i2c via the default (()) constructor and a temp object which will then be copied over to your i2c object.
And if the copy constructor isn’t written perfectly (or not at all like here) you might end up with a somewhat different object than what you expect :wink:

2 Likes

@ScruffR, one never stops learning! Had no idea about the difference. Will use this style moving forward.

1 Like

Thanks for the link and the install instructions which made it super easy to install :smiley:

It's really nice to be able to do all this within DEV :thumbsup:

@UMD Thanks for supplying your complete code, it helped out. I tried both libraries but ended up deciding on taking a different route to the data I was seeking.

I was only needing to use the library to get access to 3 bytes of data so it was not really worth switching over from the regular wire library just for those 3 bytes when I can get them another way without having to mess with a decent amount of working code.

@RWB How did you end up solving this? I’m in a similar “need only a few byes” situation :slight_smile: