Picaxe 20x2 and Adafruit TCS34725 Color Sensor


New Member
New to the forum, tried to search for an answer but to no avail.
I am having trouble getting these things to talk.
I want to read RGB values and only getting 255.

Specs say it has 0x29 address
I am using
hi2csetup i2cmaster, %1010010, i2cslow, i2cbyte
hi2cin (b0,b1,b2,b3)

Has anybody got any ideas ?


Senior Member
Hi Graeme and Welcome,

The address look correct. Have you installed the Pullup resistors necessary for I2C Communications ?

I think communications with this device is a bit more than just hic2in b0,b1,b2,b3. Is this your complete code for testing ?

The datasheet reads:
The mechanics of accessing a specific register depends on the specific protocol used. See the section on I2C
protocols on the previous pages. In general, the COMMAND register is written first to specify the specific
control-status-data register for subsequent read/write operations.

While I have not used this specific device, a cursory look at the data sheet seems to suggest that you must first write to the command register, to specify what other register you want to read data from. My best guess is that upon power up, the device goes into a sleep state and the the program will need to enable the device and set up some control registers before, trying to receive data from the RGB registers. .

I would suggest you read the datasheet carefully and even take some notes. Maybe try to find some sample code that you can look at. Adafruit has a "wiring" library that you can download. Yes, it is in "C ", but possibly you can determine how the device is initialized and read by looking at the C code library. Maybe someone else here will come along that has experience with this device that can give more specific help.


Senior Member
Welcome to the forum

Try address: %01010010

Search on 0x9 and you will find that Picaxe address is shifted left compared to Arduino


Technical Support
Staff member
You need to have a read of the datasheet:
pages 13,14,15
give the clues, it should be something like this (untested)

hi2csetup i2cmaster, %01010010, i2cslow, i2cbyte

hi2cout %10100000,(%00000001) 'register 0 is enable
pause 3
hi2cout %10100000,(%00000011) 'register 0 is enable

pause 3
hi2cin %10110100,(clear_low,clear_high)
pause 3
hi2cin %10110110,(red_low,red_high)
pause 3
hi2cin %10111000,(green_low,green_high)
pause 3
hi2cin %10111010,(blue_low, blue_high)

The very bizarre thing is that each register address appears to need to be %101rrrrr, where r is the register address.
So regsiter 0 (enable) is not address 0 at all, it is address %10100000


New Member
Thanks Guys...
You's are the best :)
'Technical's' code works.
I got a solution AND I understand it.


New Member
Many thanks, saved me quite some hair pulling! That %101rrrrr was catching me out as well for my new Lightlog prototype board I was just testing with this sensor (lightlogproject.org) --Gary


Senior Member

After nearly 10 years, here is an "update" to this thread, almost the only "hit" on the forum for the TCS34725 colour sensor, which is surprising as it seems quite a useful device. The particular features that I have added are an "On Demand" (or Single-Shot) measurement capability, and control of the "breakout" board via the I2C bus alone. The previous posts assume "continuous" operation, with a risk that the chip's double-buffering may supply an ADC measurement from before a recent change in the measurement conditions (or perhaps a mixture of before and after). The chip consumes only around 300 uA, even during its ADCs' conversion (a few uA in standby), but the board's illumination LED(s) can drain up to 10 mA, so it may be worthwhile to switch off when not needed.

The TCS34725 chip is a 3.3 volt SMD package only 2.0 x 2.4 mm, which is not very convenient for a typical hobbyist, so the Adafruit-style 20 mm square breakout board includes a 3.3v regulator (with output to the 0.1 inch inch pin-header), I2C bus level shifters and a FET-controlled white LED (Active High input). Personally I prefer the smaller "rectangular" version (11 x 21 mm plus mounting "tabs") , achieved by assembling the SMD components on both sides of the board, which has the same 7-pin header arrangement, but two LEDs, one each side of the sensor. The Active Low (i.e. Idle High) Interrupt is brought out to the header, adjacent to the LED pin at one end, so the two can be linked such that the Interrupt automatically turns OFF the LED(s) when the ADC measurement completes. Then the LED(s) can be turned ON by Clearing the Interrupt (via I2C bus command) or activated continuously by disabling Interrupts (the Default). The Interrupt and Ready Status can be also read via the I2C bus.

Thanks are due to "Technical" for decyphering the rather obscure data sheet, which also appears to have at least one error: The Default (power-up) value for registers 1 and 3 is quoted as $FF (which gives a minimum time for the ADC conversion and the optional "Wait" period). However, $FFs are only loaded into registers 2 (unused) and 3, thus the Default ADC register setting is 0, giving a maximum ADC integration time of over 600 ms, (the Wait is disabled). To avoid confusion between the Command, Config and Control register functions, I have renamed the CONTROL register GAIN (because the amplifier gain is all that it controls) and was tempted to change CONFIG to LONGWAIT (the only flag that it contains). Also, I prefer to name the "Clear" Filter as "White", obviating the data sheet's: "The Clear channel interrupt clear special function clears any pending interrupt and is self-clearing". Incidentally, the reference to "self-clearing" does not appear to be explained, however, if Interrupts are Enabled but the Clear command is NOT sent, then the INT line pulses to Idle High very briefly (flashing the LED ON if connected as above), but NOT during the ADC conversion period. :(

The program below includes optional subroutines to measure the power supply voltage (as I normally use batteries) and to report the contents of all the registers, before any are changed by the program. The LED can be set permanently ON, or OFF, or switching automatically for the ADC cycle, but the delay Timer (up to 8 seconds) and the White High and Low Threshold Interrupt/flag features are not implemented, since they seem dependent on the desired application. Also included is a simple estimation of the subjective colour, which uses only the hue (RGB ratio) because the luminance can vary enormously with the ambient light level and object distance. However, the Red/Orange (or Brown) discrimination is marginal because their hues are very similar, and the White/Yellow separation can be difficult, if significant ambient light is present (due to variations in its colour temperature). Strictly, the White should be named Grey, since the Luminance is normalised (i.e. ignored) so that even a Black surface may be reported as "White" ! Suggestions for an algorithm to improve the identification of these colours (or the Colour Temperature in general) would be welcomed.

; TCS34725  RGBW colour sensor. AllyCat, March 2024
; LED auto-controlled by interrupt pin.  Code waits for updated ADC values. 
#picaxe 14m2                    ; Or any other M2
#terminal 4800
#DEFINE ACTIVELED        ; ) LED Activated only during the ADC conversion period
; #DEFINE LEDON          ; }- If neither defined, then LED flashes briefly (not during the ADC conversion)

symbol I2CADDR = $52            ; 8-bit Slave address of TCS34725
symbol COMMAND = $A0    ; WO    ; Auto Increment Register address = $A0, Register (sub-)Address 0
symbol ENABLE     = $A0    ; R/W    ; (Active Low) INTERRUPT = 16 , WAIT = 8 , ADC = 2 , POWER = 1
symbol ATIME     = $A1            ; ADC Integration Time (256-ATIME) * 2.4ms ($D0=150ms, fs=65535)
symbol WTIME     = $A3            ; Wait time  (256 - WTIME) * 2.4ms ($AD=200ms)
symbol CONFIG     = $AD            ; 0= WAIT*1 , 2= WAIT*12 (28.8 ms units above)
symbol GAIN     = $AF            ; 0= GAIN*1, 1= GAIN*4, 2= GAIN*16, 3= GAIN*60 (CONTROL Register)
symbol DEVID     = $B2    ; RO    ; 68 ($44) or 77 ($4D)
symbol STATUS     = $B3            ; INTERRUPT=16 , ADCDONE=1
symbol WDATAL     = $B4            ; Low Byte of White Register (Datasheet uses CLEAR) .....
symbol BDATAH    = $BB            ; High Byte of Blue Register
symbol CLRINT     = $E6            ; Clear the Interrupt Flag (Special Function)
symbol REGPTR = 6            ; White Low Byte address Pointer in RAM
symbol WhiteLo = b6
symbol White = w3                ; 16-bit "Clear Filter" register address (Low byte first)
symbol Red = w4                ; Red Filter register
symbol Green = w5                ; Green Filter
symbol Blue = w6                ; Blue Filter
    pause 2000
    call getvdd                    ; Report the Power Supply voltage (OPTIONAL)
    call startup                ; Report the Softstart or Hard Reset (Default) conditions (OPTIONAL)
;    hi2cin DEVID,(b1)
;    sertxd(cr,lf,"ID(68/77)= ",#b1)
    hi2cout ATIME,($EF)                 ; 40ms ADC Integration time
    hi2cout GAIN,(2)                    ; * 16 (Named CONTROL register in Data Sheet)
    hi2cout ENABLE,(%00000001)     ; Power On
    pause 2                                ; 2.4 ms required for Oscillator to start
    hi2cout ENABLE,(%00000011)     ; Interrupt Disabled (Idle High, so can turn LED ON), Run ADC
    hi2cout ENABLE,(%00010011)     ; Interrupt and ADC On
    hi2cout (CLRINT)                    ; Clear Interrupt (Idle High) to turn LED(s) ON
    sertxd(" Waiting")                ; Around 25 ms to transmit this
        sertxd(".")                        ; Approx 6ms for each subsequent dot
        hi2cin STATUS,(b1)
    loop until b1 > 16                ; Wait for Interrupt+ADC completion
    bptr = REGPTR                        ; Generic RAM location
    hi2cin WDATAL,(@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptr)
;    hi2cin WDATAL,(b6,b7,b8,b9,b10,b11,b12,b13)            ; Explicit variables
    Green = Green ** 50000                ; Scale down to 75-80% mainly for Orange/Red discrimination
    w7 = w4 + w5 + w6    / 100 min 1        ; RGB Sum to calculate percentages (avoiding 0 divisor)
    w4 = w4 / w7 : w5 = w5 / w7 : w6 = w6 / w7                ; Calculate relative percentages
    sertxd(cr,lf,"RGB%sW= ",#w4,",",#w5,",",#w6," s=",#w7," W=",#w3)  ; Percentages, scaling and White channel
    if Blue > 40 then : sertxd(" Blue") : goto done : endif
    if Green > 40 then : sertxd(" Green") : goto done : endif
    if Red > 50 then
        if Blue < Green then : sertxd(" Orange") : goto done : endif             ; The marginal one :-(
        sertxd(" Red") : goto done
    if Blue < 27 then : sertxd(" Yellow") : goto done : endif
    sertxd(" White")
    pause 3000

sertxd(cr,lf,"Registers 0-$0F (R/W) | $10-$1B (RO):",cr,lf)
    for b0 = COMMAND to BDATAH
        hi2cin b0,(b1)
        if b0 = GAIN then
    next b0
    hi2cin ATIME,(b1)
    w1 = 256 - b1 * 240 / 100                 ; Time in ms
    sertxd(cr,lf,"ADC Integration Time = ",#w1," ms")
getvdd:                                ; Test the power supply voltage
symbol CALVDD = 52429            ; 1024*1.024*1000/20  (ADC steps * Ref V / Resolution in mV)
calibadc10 w1                    ; Measure FVR (nominal 1.024 v) relative to Vdd (1024 steps)
    w2 = w1 / 2 + CALVDD            ; Effectively round up CALVDD by half a (result) bit
    w2 = w2 / w1                    ; Take the reciprocal to calculate (half) Vdd (tens of mV)
    calibadc10 w1                ; Read the value again because noise might be present :)
    w1 = CALVDD / w1 + w2        ; Calculate Vdd/2 again and add in the first value
    sertxd(cr,lf,"Vdd= ",#w1,"0 mV")

Vdd= 4980 mV
Registers 0-$0F (R/W) | $10-$1B (RO):
ADC Integration Time = 614 ms
Initialising. Waiting.....
RGB%sW= 32,31,36 s=190 W=17408 White Waiting.....
RGB%sW= 32,31,36 s=189 W=17408 White
Vdd= 5000 mV
Registers 0-$0F (R/W) | $10-$1B (RO):
ADC Integration Time = 40 ms
Initialising. Waiting.....
RGB%sW= 32,31,36 s=187 W=17408 White Waiting.....
RGB%sW= 56,19,24 s=53 W=4169 Red Waiting.....
RGB%sW= 25,46,28 s=36 W=3919 Green Waiting.....
RGB%sW= 25,27,47 s=52 W=4247 Blue Waiting.....
RGB%sW= 58,21,20 s=77 W=7194 Orange Waiting.....
RGB%sW= 40,37,21 s=184 W=17408 Yellow Waiting.....
Cheers, Alan.


Senior Member
THANK YOU ALLYCAT! You saved my bacon tonight. I was just contacted by a panicked high school advisor yesterday. A team of graduating students was left high & dry by some sketchy tech company that directed them buy a bunch of Arduino hardware to run a color sensor. The students have been working on this project for months and are on a tight schedule to present their project at MIT in June. But when something didn't work as anticipated, the tech company bailed on them last week, saying they were out of time. Shameful! I did a Zoom video meeting with the students and offered to try to help. I ordered the smallest color sensors Amazon had, which were delivered today. I had zero experience with this I2C sensor but I just loaded your program above in an 08M2 and it just worked first time. THANK YOU, I can whip up a circuit board to get the students back on track.

Picaxe: 1 Arduino: 0



Senior Member
The all-girl students team made a pink 3D printed case and I put the guts inside, the unit works great. Picaxe FTW!