Grove Color Sensor V2

Hi,

ich habe mich mal daran versucht, den Grove Color Sensor V2 zu unterstützen und mich dabei an der originalen Library von Seeed-Studio orientiert, um das ganze als „custom block“ zu erzeugen.

Problematisch ist die fehlende Unterstützung von float und double als Zahlenformat, aber auch ein fehlendes bitweises not ("~").

Nichts desto trotz, kann ich erkennen, ob der Sensor angeschlossen ist und bekomme auch RGB-Werte. Aber so richtig kann ich die noch nicht interpretieren. Zumindest die RGB-LED ändert die Farbe bei einem Farbwechsel, wenn sie auch nicht die richtig Farbe anzeigt… ;-).

Vielleicht hat ja jemand Lust, da mal mit drauf zu schauen…


/**
 * Use this file to define custom functions and blocks.
 * Read more at https://makecode.calliope.cc/blocks/custom
 * 
 * This is a very close clone of the Seed-Studio library
 * https://github.com/Seeed-Studio/Grove_I2C_Color_Sensor_TCS3472/blob/master/Adafruit_TCS34725.cpp
 */

const TCS34725_ADDRESS = 0x29;

const TCS34725_COMMAND_BIT = 0x80;

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

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

enum tcs34725Gain_t {
    TCS34725_GAIN_1X = 0x00,   /**<  No gain  */
    TCS34725_GAIN_4X = 0x01,   /**<  4x gain  */
    TCS34725_GAIN_16X = 0x02,   /**<  16x gain */
    TCS34725_GAIN_60X = 0x03    /**<  60x gain */
}

/**
 * Custom blocks
 */
//% weight=100 color=#0fbc11 icon=""
namespace color_Sensor {
    let _tcs34725Initialised = false;
    let _tcs34725IntegrationTime = tcs34725IntegrationTime_t.TCS34725_INTEGRATIONTIME_50MS;
    let _tcs34725Gain = tcs34725Gain_t.TCS34725_GAIN_4X;

    function write8(reg: number, value: number): void {
        pins.i2cWriteNumber(TCS34725_ADDRESS, TCS34725_COMMAND_BIT | reg, NumberFormat.Int8LE);
        pins.i2cWriteNumber(TCS34725_ADDRESS, value & 0xFF, NumberFormat.Int8LE)
    }

    function read8(reg: number): number {
        pins.i2cWriteNumber(TCS34725_ADDRESS, TCS34725_COMMAND_BIT | reg, NumberFormat.Int8LE);
        return pins.i2cReadNumber(TCS34725_ADDRESS, NumberFormat.Int8LE);
    }

    function read16(reg: number): number {
        pins.i2cWriteNumber(TCS34725_ADDRESS, TCS34725_COMMAND_BIT | reg, NumberFormat.Int8LE);
        return pins.i2cReadNumber(TCS34725_ADDRESS, NumberFormat.UInt16BE);
    }

    /**
     * Turn the device on
     */
    function enable() {
        write8(TCS34725_ENABLE, TCS34725_ENABLE_PON);
        basic.pause(3);
        write8(TCS34725_ENABLE, TCS34725_ENABLE_PON | TCS34725_ENABLE_AEN);
    }

    /**
     * Turn the device off to save power
     */
    function disable() {
        let reg = read8(TCS34725_ENABLE);
        write8(TCS34725_ENABLE, reg & 0xfc); // ~(TCS34725_ENABLE_PON | TCS34725_ENABLE_AEN));
    }

    /**
     * Begin using the sensor, will be called automatically if necessary,
     * but can be used to check if a sensor is connected.
     * 
     * @param gain Set the timing register to a value from 0-255
     */
    //% block

    export function begin(): boolean {
        /* Make sure we're actually connected */
        let 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;
    }

    /**
     * Set integration time
     * @param it Set the integration time from 0-255
     */
    //% block
    export function setIntegrationTime(it: number): void {
        if (!_tcs34725Initialised) begin();

        /* Update the timing register */
        write8(TCS34725_ATIME, it);

        /* Update value placeholders */
        _tcs34725IntegrationTime = it;
    }

    /**
     * Set gain
     * @param gain Set the timing register to a value from 0-255
     */
    //% block
    export function setGain(gain: number): void {
        if (!_tcs34725Initialised) begin();

        /* Update the timing register */
        write8(TCS34725_CONTROL, gain);

        /* Update value placeholders */
        _tcs34725Gain = gain;
    }

    /**
     * Get RGB data from sensor
     */
    //% block
    //% blockSetVariable = number
    export function getRgbData(): number {
        if (!_tcs34725Initialised) begin();

        let c = pins.map(read16(TCS34725_CDATAL), 0, 65535, 0, 255);
        let r = pins.map(read16(TCS34725_RDATAL), 0, 65535, 0, 255);
        let g = pins.map(read16(TCS34725_GDATAL), 0, 65535, 0, 255);
        let b = pins.map(read16(TCS34725_BDATAL), 0, 65535, 0, 255);

        /* Set a delay for the integration time */
        switch (_tcs34725IntegrationTime) {
            case tcs34725IntegrationTime_t.TCS34725_INTEGRATIONTIME_2_4MS:
                basic.pause(3);
                break;
            case tcs34725IntegrationTime_t.TCS34725_INTEGRATIONTIME_24MS:
                basic.pause(24);
                break;
            case tcs34725IntegrationTime_t.TCS34725_INTEGRATIONTIME_50MS:
                basic.pause(50);
                break;
            case tcs34725IntegrationTime_t.TCS34725_INTEGRATIONTIME_101MS:
                basic.pause(101);
                break;
            case tcs34725IntegrationTime_t.TCS34725_INTEGRATIONTIME_154MS:
                basic.pause(154);
                break;
            case tcs34725IntegrationTime_t.TCS34725_INTEGRATIONTIME_700MS:
                basic.pause(700);
                break;
        }
        return basic.rgbw(r, g, b, 0); //r, g, b, c;
    }

    // This function uses float (and powf()) which is not supported by Calliope
    //     function calculateColorTemperature(r: number, g: number, b: number): number {
    //         //X, Y, Z;      /* RGB to XYZ correlation      */
    //         //xc, yc;       /* Chromaticity co-ordinates   */
    //         //n;            /* McCamy's formula            */
    //         //cct;
    // 
    //         /* 1. Map RGB values to their XYZ counterparts.    */
    //         /* Based on 6500K fluorescent, 3000K fluorescent   */
    //         /* and 60W incandescent values for a wide range.   */
    //         /* Note: Y = Illuminance or lux                    */
    //         let X = (-0.14282 * r) + (1.54924 * g) + (-0.95641 * b);
    //         let Y = (-0.32466 * r) + (1.57837 * g) + (-0.73191 * b);
    //         let Z = (-0.68202 * r) + (0.77073 * g) + (0.56332 * b);
    // 
    //         /* 2. Calculate the chromaticity co-ordinates      */
    //         let xc = (X) / (X + Y + Z);
    //         let yc = (Y) / (X + Y + Z);
    // 
    //         /* 3. Use McCamy's formula to determine the CCT    */
    //         let n = (xc - 0.3320) / (0.1858 - yc);
    // 
    //         /* Calculate the final CCT */
    //         let cct = (449.0 * powf(n, 3)) + (3525.0 * powf(n, 2)) + (6823.3 * n) + 5520.33;
    // 
    //         /* Return the results in degrees Kelvin */
    //         return cct;
    //     }

    // This function uses float which is not supported by Calliope
    //     function calculateLux(r: number, g: number, b: number): number {
    //         /* This only uses RGB ... how can we integrate clear or calculate lux */
    //         /* based exclusively on clear since this might be more reliable?      */
    //         let illuminance = (-0.32466 * r) + (1.57837 * g) + (-0.73191 * b);
    // 
    //         return illuminance;
    //     }


    function setInterrupt(i: boolean): void {
        let r = read8(TCS34725_ENABLE);
        if (i) {
            r |= TCS34725_ENABLE_AIEN;
        } else {
            r &= notTCS34725_ENABLE_AIEN; // ~TCS34725_ENABLE_AIEN; Calliope does not have bitwise not?
        }
        write8(TCS34725_ENABLE, r);
    }

    function clearInterrupt(): void {
        pins.i2cWriteNumber(TCS34725_ADDRESS, TCS34725_COMMAND_BIT | 0x66, NumberFormat.Int8LE)
    }

    function setIntLimits(low: number, high: number): void {
        write8(0x04, low & 0xFF);
        write8(0x05, low >> 8);
        write8(0x06, high & 0xFF);
        write8(0x07, high >> 8);
    }
}
if (color_Sensor.begin()) {
    basic.showString("T")
} else {
    basic.showString("F")
}
basic.pause(1000)
basic.forever(() => {
    basic.setLedColor(color_Sensor.getRgbData())
})
1 Like

Hallo Balu,

Ich wusste da war noch was: der envirobit benutzt den gleichen Sensor https://github.com/pimoroni/pxt-envirobit/blob/master/envirobit.ts
https://www.youtube.com/watch?v=cClwjpsvOSk