Help debugging I2C

Trying to port the code for the TCS3472 color sensor over to the Electric Imp. Reference here: https://github.com/adafruit/Adafruit_TCS34725

This code returns a value from the sensor, but the value stays the same every time when it should be constantly moving around like a sensor value.

`//TCS34725 imp port

const TCS34725_ADDRESS = 0x29;

const TCS34725_COMMAND_BIT ="\x80";

const TCS34725_ENABLE ="\x00";
const TCS34725_ENABLE_AIEN ="\x10"; /* RGBC Interrupt Enable /
const TCS34725_ENABLE_WEN ="\x08"; /
Wait enable - Writing 1 activates the wait timer /
const TCS34725_ENABLE_AEN ="\x02"; /
RGBC Enable - Writing 1 actives the ADC, 0 disables it /
const TCS34725_ENABLE_PON ="\x01"; /
Power on - Writing 1 activates the internal oscillator, 0 disables it /
const TCS34725_ATIME ="\x01"; /
Integration time /
const TCS34725_WTIME ="\x03"; /
Wait time (if TCS34725_ENABLE_WEN is asserted /
const TCS34725_WTIME_2_4MS ="\xFF"; /
WLONG0 = 2.4ms WLONG1 = 0.029s /
const TCS34725_WTIME_204MS ="\xAB"; /
WLONG0 = 204ms WLONG1 = 2.45s /
const TCS34725_WTIME_614MS ="\x00"; /
WLONG0 = 614ms WLONG1 = 7.4s /
const TCS34725_AILTL ="\x04"; /
Clear channel lower interrupt threshold /
const TCS34725_AILTH ="\x05"
const TCS34725_AIHTL ="\x06"; /
Clear channel upper interrupt threshold /
const TCS34725_AIHTH ="\x07"
const TCS34725_PERS ="\x0C"; /
Persistence register - basic SW filtering mechanism for interrupts /
const TCS34725_PERS_NONE = 0x0; /
Every RGBC cycle generates an interrupt /
const TCS34725_PERS_1_CYCLE = 0x1; /
1 clean channel value outside threshold range generates an interrupt /
const TCS34725_PERS_2_CYCLE = 0x2; /
2 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_3_CYCLE = 3; /
3 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_5_CYCLE = 4; /
5 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_10_CYCLE = 5; /
10 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_15_CYCLE = 6; /
15 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_20_CYCLE = 7; /
20 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_25_CYCLE = 8; /
25 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_30_CYCLE = 9; /
30 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_35_CYCLE = 0xA; /
35 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_40_CYCLE = 0xB; /
40 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_45_CYCLE = 0xC; /
45 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_50_CYCLE = 0xD; /
50 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_55_CYCLE = 0xE; /
55 clean channel values outside threshold range generates an interrupt /
const TCS34725_PERS_60_CYCLE = 0xF; /
60 clean channel values outside threshold range generates an interrupt /
const TCS34725_CONFIG ="\x0D";
const TCS34725_CONFIG_WLONG ="\x02"; /
Choose between short and long (12x wait times via TCS34725_WTIME /
const TCS34725_CONTROL ="\x0F"; /
Set the gain level for the sensor /
const TCS34725_ID ="\x12"; /
0x44 = TCS34721/TCS34725, 0x4D = TCS34723/TCS34727 /
const TCS34725_STATUS ="\x13";
const TCS34725_STATUS_AINT ="\x10"; /
RGBC Clean channel interrupt /
const TCS34725_STATUS_AVALID ="\x01"; /
Indicates that the RGBC channels have completed an integration cycle /
const TCS34725_CDATAL ="\x14"; /
Clear channel data /
const TCS34725_CDATAH ="\x15";
const TCS34725_RDATAL ="\x16"; /
Red channel data /
const TCS34725_RDATAH ="\x17";
const TCS34725_GDATAL ="\x18"; /
Green channel data /
const TCS34725_GDATAH ="\x19";
const TCS34725_BDATAL ="\x1A"; /
Blue channel data */
const TCS34725_BDATAH ="\x1B";

const TCS34725_INTEGRATIONTIME_2_4MS = 0xFF; /< 2.4ms - 1 cycle - Max Count: 1024 */
const TCS34725_INTEGRATIONTIME_24MS = 0xF6; /
< 24ms - 10 cycles - Max Count: 10240 */
const TCS34725_INTEGRATIONTIME_50MS = “\xEB”; /< 50ms - 20 cycles - Max Count: 20480 */
const TCS34725_INTEGRATIONTIME_101MS = 0xD5; /
< 101ms - 42 cycles - Max Count: 43008 */
const TCS34725_INTEGRATIONTIME_154MS = 0xC0; /< 154ms - 64 cycles - Max Count: 65535 */
const TCS34725_INTEGRATIONTIME_700MS = “\x00”; /
< 700ms - 256 cycles - Max Count: 65535 */

const TCS34725_GAIN_1X = “\x00”; /< No gain */
const TCS34725_GAIN_4X = 0x01; /
< 4x gain */

const TCS34725_GAIN_16X = 0x02; /< 16x gain */
const TCS34725_GAIN_60X = 0x03; /
< 60x gain */

function write8(REG_ADDR, REG_VAL) {
i2c.write(i2cAddr, REG_ADDR);
i2c.write(i2cAddr, REG_VAL);
}

function read16(REG_ADDR) {
//server.log("I2C read error: " + i2c.readerror());
local word = i2c.read(i2cAddr, REG_ADDR, 2);
// local intVal = hexStringToInt(word);
// server.log(“intVal =” + intVal);
local REG_VAL = (word[0] << 8) + word[1];
server.log(REG_VAL);
return REG_VAL;
}

function read8(REG_ADDR) {
//server.log("I2C read error: " + i2c.readerror());
local word = i2c.read(i2cAddr, REG_ADDR, 1);
// local intVal = hexStringToInt(word);
// server.log(“intVal =” + intVal);
local REG_VAL = word[0];
server.log(REG_VAL);
return REG_VAL;
}

function tcsEnable(){
write8(TCS34725_ENABLE, TCS34725_ENABLE_PON)
imp.sleep(3);
write8(TCS34725_ENABLE, “\x03”)
}

function getRawData()
{
local _c = read16(TCS34725_CDATAL);
local _r = read16(TCS34725_RDATAL);
local _g = read16(TCS34725_GDATAL);
local _b = read16(TCS34725_BDATAL);

/* Set a delay for the integration time */
// switch (_tcs34725IntegrationTime)
// {
// case TCS34725_INTEGRATIONTIME_2_4MS:
// imp.sleep(.003);
// break;
// case TCS34725_INTEGRATIONTIME_24MS:
// imp.sleep(.024);
// break;
// case TCS34725_INTEGRATIONTIME_50MS:
// imp.sleep(.050);
// break;
// case TCS34725_INTEGRATIONTIME_101MS:
// imp.sleep(.101);
// break;
// case TCS34725_INTEGRATIONTIME_154MS:
// imp.sleep(.154);
// break;
// case TCS34725_INTEGRATIONTIME_700MS:
// imp.sleep(.700);
// break;
// }
server.log("_r = " + _r);
}

// Set up alias for i2c and set bus to 100kHz
i2c <- hardware.i2c12;
i2c.configure(CLOCK_SPEED_100_KHZ);

// Set sensor’s address by shifting 7-bit address 1 bit leftward as per imp I2C spec
i2cAddr <- TCS34725_ADDRESS << 1;

tcsEnable();
i2c.write(i2cAddr, TCS34725_CONTROL)
write8(TCS34725_CONTROL, “\x3F”);
getRawData();
`

This code just returns a static values for the raw rgbc values.

Someone pointed out that this sensor utilizes Repeated state i2c state. Does the electric imp support this? Page 12 of the datasheet: https://www.adafruit.com/datasheets/TCS34725.pdf

Repeated start? Yes the imp does that implicitly when you do a read operation.

Ah, I see your problem. The imp’s i2c is managed, so you don’t need to issue operations a byte at a time, eg write8 should be:

`function write8(REG_ADDR, REG_VAL) {
   i2c.write(i2cAddr, REG_ADDR+REG_VAL);
}`

…the way you were doing it was never writing any value to a register. I2C devices generally interpret the first byte after the device address as a subaddress byte, so the first i2c.write was setting the address pointer (but writing no data) and the second i2c.write was setting the address pointer to REG_VAL.

Can you explain what it means that the imp’s i2c is managed?

I’m still having issues after implementing your suggestion:

Equivalent code from Adafruit’s arduino library:

`boolean Adafruit_TCS34725::begin(void)
{
Wire.begin();

/* Make sure we’re actually connected */
uint8_t x = read8(TCS34725_ID);
if ((x != 0x44) && (x != 0x10))
{
return false;
}
_tcs34725Initialised = true;

/* Set default integration time and gain */
setIntegrationTime(_tcs34725IntegrationTime);
setGain(_tcs34725Gain);

/* Note: by default, the device is in power down mode on bootup */
enable();

return true;
}

void Adafruit_TCS34725::enable(void)
{
write8(TCS34725_ENABLE, TCS34725_ENABLE_PON);
delay(3);
write8(TCS34725_ENABLE, TCS34725_ENABLE_PON | TCS34725_ENABLE_AEN);
}
void Adafruit_TCS34725::getRawData (uint16_t *r, uint16_t *g, uint16_t *b, uint16_t *c)
{
if (!_tcs34725Initialised) begin();

*c = read16(TCS34725_CDATAL);
*r = read16(TCS34725_RDATAL);
*g = read16(TCS34725_GDATAL);
*b = read16(TCS34725_BDATAL);

/* Set a delay for the integration time */
switch (_tcs34725IntegrationTime)
{
case TCS34725_INTEGRATIONTIME_2_4MS:
delay(3);
break;
case TCS34725_INTEGRATIONTIME_24MS:
delay(24);
break;
case TCS34725_INTEGRATIONTIME_50MS:
delay(50);
break;
case TCS34725_INTEGRATIONTIME_101MS:
delay(101);
break;
case TCS34725_INTEGRATIONTIME_154MS:
delay(154);
break;
case TCS34725_INTEGRATIONTIME_700MS:
delay(700);
break;
}
}
`

And my imp device code for the same:

`
function write8(REG_ADDR, REG_VAL) {
i2c.write(i2cAddr, REG_ADDR+REG_VAL);
}

function read16(REG_ADDR) {
//server.log("I2C read error: " + i2c.readerror());
local word = i2c.read(i2cAddr, REG_ADDR, 2);
// local intVal = hexStringToInt(word);
// server.log(“intVal =” + intVal);
local REG_VAL = (word[0] << 8) + word[1];
server.log(REG_VAL);
return REG_VAL;
}

function tcsEnable(){
write8(TCS34725_ENABLE, TCS34725_ENABLE_PON);
imp.sleep(3);
write8(TCS34725_ENABLE, 0x02);
}

function getRawData()
{
local _c = read16(TCS34725_CDATAL);
local _r = read16(TCS34725_RDATAL);
local _g = read16(TCS34725_GDATAL);
local _b = read16(TCS34725_BDATAL);

server.log("_r = " + _r);
}

// Set up alias for i2c and set bus to 100kHz
i2c <- hardware.i2c12;
i2c.configure(CLOCK_SPEED_100_KHZ);

// Set sensor’s address by shifting 7-bit address 1 bit leftward as per imp I2C spec
i2cAddr <- TCS34725_ADDRESS << 1;

tcsBegin(TCS34725_INTEGRATIONTIME_700MS, TCS34725_GAIN_1X);

function poll()
{
// Read the pin and log its value

getRawData();

// Wake up in 0.1 seconds and do it again

imp.wakeup(0.7, poll);

}

// Start the loop

poll();`

Just returns _r = 5632 All day long.

Also, a read from TCS34725_ID returns 18 when it should be 0x44 or 0x10. i2c.readerror returns 0.

Saleae make a logic analyser which is fantastic for troubleshooting I2C and SPI buses. It’s a real time saver to see the data on the wire (displayed nicely on a computer screen) rather than second guessing the problem in software.

It’s not much help if you don’t have one to hand but I recommend getting one for the toolbox as they are very useful for these kinds of problems.

@Hugo didn’t you write some code for this chip when you were playing with detecting coffee cup colors?

@rocketfire Just ordered up a Logic 8. Thanks for the recommendation.

Ah yes @brandon, you’re right, I’d forgotten that I’d used that chip a couple of years ago… here’s my code for that chip that worked fine.

Reading my code & yours, I can now see the critical difference… the top bit of the subaddress always needs to be set, it appears… (I think you missed that every command ORs in TCS34725_COMMAND_BIT to set bit 8 of the subregister… of course, you could just change the register definitions to also have that bit set!)

`const TCS_ADDR = 0x52;
const TCS34725_ENABLE = 0x00;
const TCS34725_ENABLE_PON = 0x01;
const TCS34725_ENABLE_AEN = 0x02;
const TCS34725_ATIME = 0x01;
const TCS34725_INTEGRATIONTIME_2_4MS = 0xff;
const TCS34725_INTEGRATIONTIME_24MS = 0xf6;
const TCS34725_INTEGRATIONTIME_50MS = 0xeb;
const TCS34725_INTEGRATIONTIME_101MS = 0xd5;
const TCS34725_INTEGRATIONTIME_154MS = 0xc0;
const TCS34725_INTEGRATIONTIME_700MS = 0x00;
const TCS34725_CONTROL = 0x0f;
const TCS34725_GAIN_1X = 0x00;
const TCS34725_GAIN_4X = 0x01;
const TCS34725_GAIN_16X = 0x02;
const TCS34725_GAIN_60X = 0x03;

const TCS34725_ID = 0x12;
const TCS34725_CDATAL = 0x14;
const TCS34725_RDATAL = 0x16;
const TCS34725_GDATAL = 0x18;
const TCS34725_BDATAL = 0x1a;

// Illumination LED connected to pin 5, I2C on pins 1 & 2
ledcontrol <- hardware.pin5;
i2c <- hardware.i2c12;

ledcontrol.configure(DIGITAL_OUT);
ledcontrol.write(0);
i2c.configure(CLOCK_SPEED_100_KHZ);

function tcs_write(reg, value) {
i2c.write(TCS_ADDR, format("%c%c", reg|0x80, value));
}

function tcs_read(reg) {
return i2c.read(TCS_ADDR, format("%c", reg|0x80), 1)[0];
}

function tcs_read16(reg) {
local r = i2c.read(TCS_ADDR, format("%c", reg|0x80), 2);
return (r[1]<<8)|r[0];
}

// read chip id
server.log(format(“Chip ID=%02x”, tcs_read(TCS34725_ID)));

// set integration time
tcs_write(TCS34725_ATIME, TCS34725_INTEGRATIONTIME_50MS);

// set gain
tcs_write(TCS34725_CONTROL, TCS34725_GAIN_1X);

// enable chip
tcs_write(TCS34725_ENABLE, TCS34725_ENABLE_PON);
imp.sleep(0.003);
tcs_write(TCS34725_ENABLE, TCS34725_ENABLE_PON | TCS34725_ENABLE_AEN);

function poll() {
local c = tcs_read16(TCS34725_CDATAL);
local r = tcs_read16(TCS34725_RDATAL);
local g = tcs_read16(TCS34725_GDATAL);
local b = tcs_read16(TCS34725_BDATAL);

server.log(format("c=%4x r=%4x g=%4x b=%4x", c, r, g, b));

imp.wakeup(1, poll);

}

// Turn LED on and start showing RGB data
ledcontrol.write(1);
poll();
`

@Hugo works as advertised! Thanks for saving me the time. Can you explain to me the difference between “\xFF” and 0xFF in sqrrl?

“\xFF” is a string with a single byte value of 255. 0xFF is a number, 255.