New proposed libraries: SoftI2CMaster and TCS34725

I’ve got a couple of libraries I think might be useful to the Spark community. I’m not sure how to submit them, so I’m posting something here in the hope that someone at Spark can help. They are available at the following github repository:

https://github.com/leo3linbeck/spark-softi2c-tcs347265

Here’s a quick summary of the two libraries:

1. SoftI2CMaster. This allows for I2C communication using any two arbitrary Spark pins. This can be handy when connecting multiple devices to the Spark; if, say, two sensors have the same I2C address, you can’t use them on the same bus, but you can set up separate busses for each in software using SoftI2CMaster. Also, some devices don’t play all that nicely with others on the bus, or have different timing needs (e.g. if you need a slower bus because of longer, out-of-spec bus lengths), so you can give them their own bus.

It was adapted from an Arduino library originally written by Tod E. Kurt (http://todbot.com/blog/). Repository at https://github.com/todbot/SoftI2CMaster

Use is pretty straightforward: you call the constructor function, passing the two pins as parameters:

i2c = SoftI2CMaster(sdaPin, sclPin);

This returns an SoftI2CMaster type, which you can squirrel away in a variable for use in other functions.

To write a byte, you use a pattern like this:

i2c.beginWriteTransmission(address, register);
i2c.write(byte_var);
i2c.endTransmission();

To read a byte, you call:

i2c.beginReadTransmission(address, register);
r = i2c.readLast();
i2c.endTransmission();
return r;

To read 2 bytes, you use a pattern like this:

i2c.beginReadTransmission(address, register);
t = i2c.read();
x = i2c.readLast();
i2c.endTransmission();
x <<= 8;
x |= t;
return x;

I’ve had pretty good performance using this library on a device that uses a couple of TCS34725 color sensors. Because the TCS34725 has a single address, I couldn’t use the regular I2C bus on the Spark. You can see from the second library below how the SoftI2CMaster library is used in a more detailed example.

Another use case where this might be valuable is where you need to use the built-in I2C bus pins for other purposes, but you still need to provide an I2C bus for other sensors, etc.

One more point: there is a define, i2cbitdelay, which is in microseconds and determines the frequency of the bus. It is set at 50, but if you need higher or lower frequencies, you can simply adjust this parameter.

2. TCS34725. This library allows use of one or more TCS34725 sensors. Adafruit sells a couple of nice breakout boards using this sensor, and this library should work fine with those sensors. (The library has been more thoroughly tested on my device, which has a custom PCB that has two sensor chips directly mounted, but a lot of the support circuitry is similar.) This library was adapted from the Adafruit library: https://github.com/adafruit/Adafruit_TCS34725

Anyhoo, here’s how to use that library. First, construct an object and then initialize it:

tcs = TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_1X, sdaPin, sclPin);
if (tcs.begin()) {
   tcs.enable();
}

The integration time and gain parameters are standard defines from the header file.

Then, when you want to take a sensor reading, use the following pattern:

uint16_t clear, red, green, blue;

tcs->getRawData(&red, &green, &blue, &clear);

There are some nice functions in the original Adafruit library that allow for calculation of lux and temperature, but I didn’t include them because I’m trying to avoid the floating point library. If you want them, it would be easy enough to copy the code over from the original Arduino library on the Adafruit site.

I’ve included a pretty simple example file.

Anyway, I hope others find these helpful. If they pass muster with the Spark PTB, that would be great too. If I’m supposed to do something else to submit these, someone just needs to lemme know how.

Cheers,
L3

6 Likes

Nice work :+1:

Submitting for the Web IDE isn’t difficult at all - even I managed :wink:

See here
http://docs.spark.io/build/#flash-apps-with-spark-build-contribute-a-library

Two things to mention in particular is that your repo needs some extra files (spark.json and LICENSE) and a standardized directory hierarchy (see some published libs for that).
And the other, if you use any other libraries and header files in your demo you should include them like this #include "lib-name/lib-name.h" otherwise the build would fail with an no such file error.

Thanks for porting the SoftI2C library to the Spark Core.

It works great with the TCS34725. But it doesn’t work with the HTU21 temperature/humidity sensor. Maybe you can help.

First I tried it with an Arduino board the HTU21 library from Sparkfun and the SoftI2C library, it works fine. The modifications are commented.

float readTemperatureHTU21D(void)
{
	//Request the temperature
	i2c.beginTransmission(HTDU21D_ADDRESS);
	i2c.write(TRIGGER_TEMP_MEASURE_NOHOLD);
	i2c.endTransmission();
	//Hang out while measurement is taken. 50mS max, page 4 of datasheet.

	delay(55);

	//Comes back in three bytes, data(MSB) / data(LSB) / Checksum
	i2c.requestFrom(HTDU21D_ADDRESS);	 // only requestFrom(HTDU21D_ADDRESS);

/* // not used from the original library, changed one line before 

//Wait for data to become available
i2c.requestFrom(HTDU21D_ADDRESS,3);
int counter = 0;
while(i2c.available() < 3)
{
counter++;
delay(1);
if(counter > 100) return 998; //Error out
}
*/

	unsigned char msb, lsb, checksum;
	msb = i2c.read();
	lsb = i2c.read();
	checksum = i2c.read();

	unsigned int rawTemperature = ((unsigned int) msb << 8) | (unsigned int) lsb;

	if(check_crc(rawTemperature, checksum) != 0) return(999); //Error out

	//sensorStatus = rawTemperature & 0x0003; //Grab only the right two bits
	rawTemperature &= 0xFFFC; //Zero out the status bits but keep them in place

	//Given the raw temperature data, calculate the actual temperature
	float tempTemperature = rawTemperature / (float)65536; //2^16 = 65536
	float realTemperature = -46.85 + (175.72 * tempTemperature); //From page 14

	return(realTemperature);  
}

I added in your library following method:

uint8_t SoftI2CMaster::requestFrom(uint8_t address)
{
    i2c_start();
    i2c_write((address<<1) | 1); // set read bit
    return 1;
} 

If I read on the I2C I only get 255.

msb = i2c.read();
lsb = i2c.read();
checksum = i2c.read();

Do you have an idea where the problem could be?

Cheers.

Thomas,

Well, I’m not sure how much help I can provide, but here’s some thoughts:

I don’t know anything about the HTU21D, but I’m assuming that the address is HTDU21D_ADDRESS and the register you’re trying to read is TRIGGER_TEMP_MEASURE_NOHOLD.

The “requestFrom” function is sort of built into the “beginReadTransmission” function in my library. So you might try something like this:

float readTemperatureHTU21D(void)
{
    uint8_t msb , lsb, chksum;

    //Request the temperature
    i2c.beginReadTransmission(HTDU21D_ADDRESS, TRIGGER_TEMP_MEASURE_NOHOLD);
    msb = i2c.read(); // ack
    lsb = i2c.read();  // ack
    chksum = i2c.readLast(); // nack
    i2c.endTransmission();

    unsigned int rawTemperature = ((unsigned int) msb << 8) | (unsigned int) lsb;

    if(check_crc(rawTemperature, chksum) != 0) return(999); //Error out

    rawTemperature &= 0xFFFC; //Zero out the status bits but keep them in place

    //Given the raw temperature data, calculate the actual temperature
    float tempTemperature = rawTemperature / (float)65536; //2^16 = 65536
    float realTemperature = -46.85 + (175.72 * tempTemperature); //From page 14

    return(realTemperature); 
}

Again, I’m not sure if this will be helpful or not. Do you have an oscilloscope that you can use to watch the pin voltages?

One final thought: I quickly checked the HTU21 data sheet and it appears to operate in I2C “fast mode”. The frequency is about 400kHz, which implies that the “i2cbitdelay” constant (#defined at the beginning of SoftI2CMaster.h) would need to be 2.5 (microseconds). This is pretty darned fast, but then it’s the maximum frequency. You might try dialing down this from 50 to see if it makes any difference.

If none of this works, lemme know and I’ll try to dream up something else (and hopefully not waste your time ;-).

Cheers,
L3

1 Like

Hi L3,

thanks for your help. You definitely don’t waste my time. I would say you waste your time for me.

It wasn’t successful. :frowning:
–> i2c.read() returns every time 255.

No, I don’t have a logic analyzer or an oscilloscope, I can’t check if there is a response on the SDA line.

I dont’t think that the problem is the frequency. I tried on the arduino board to increase and decrease the i2cbitdelay, without any difference (except the slower transmission)

But the 50ms delay between i2c.beginReadTransmission() and the first i2c.read() is necessary, the sensor needs the 50ms for the measurement.

It would be great if you have an other dream. :wink: