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())
})
2 „Gefällt mir“

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

1 „Gefällt mir“

Hi Balu,
hast Du es eigentlich mit dem Grove Color Sensor hinbekommen?
VG Dirk

1 „Gefällt mir“

Das interessiert mich auch sehr … gibts hier was neues?

1 „Gefällt mir“

Leider nein von meiner Seite aus. Mir ist leider „Leben“ dazwischen gekommen, so dass ich das erstmal nicht weiter verfolgen konnte.

Grundsätzlich hatte ich ja Antworten bekommen, ich habe nur nie prüfen können, ob die Werte reproduzierbar / korrekt sind.

1 „Gefällt mir“

Dann mal herzlichen Glückwunsch und ein paar schlaflose Nächte :smirk: - Zeit zum Experimentierten :blush:

1 „Gefällt mir“

Ne, nicht so ein neues Leben, sondern „das Leben“ allgemein… :slight_smile:

1 „Gefällt mir“

:wink: na ok … dann geht’s doch :wink:

1 „Gefällt mir“

Gibt es Neuigkeiten bei dem Projekt? Wäre super interessiert an einem Programm mit dem Farbsensor, welches die Lichtzusammensetztung auf der LED-Matrix visualisiert. Damit könnte man dann den Kids auf einfache Weise die Funktionsweise eines Satelliten erklären :slight_smile:

1 „Gefällt mir“

Hast Du schon einen Farbsensor? Soll ich die Farberkennung aus dem envirobitpaket extrahieren? https://youtu.be/cClwjpsvOSk

1 „Gefällt mir“

Der Plan war es mit diesem Farbsensor zu versuchen: http://wiki.seeedstudio.com/Grove-I2C_Color_Sensor/
Bin noch relativ neu und unerfahren mit der Programmierung von solchen Dingen. Von daher hatte ich die Hoffnung das es bereits einen fertigen Code für meine Idee gibt

1 „Gefällt mir“

Hatte die Library gerade umgebaut da stelle ich fest das Seed ja doch einen anderen Sensor verwendet. Eine schnelle Suche brachte mehrere fertige Pakete zutage:

Es sind also fertige Pakete vorhanden!

2 „Gefällt mir“

Wenn ich das richtig verstanden habe kann ich diese Pakete in MakeCode (oder auch Open Roberta Lab?) über GitHub laden, wo die dann die passenden Blöcke im Editor erzeugen richtig?
Tut mir leid für die dumme Frage, bin noch relativ neu im Umgang mit der Materie Calliope :smiley:

1 „Gefällt mir“

Ja, am besten in der Betaversion makecode.calliope.cc/beta und dann auf Erweiterungen klicken und dann die Adresse aus Github in der Suche eingeben…

1 „Gefällt mir“

perfekt das hat soweit geklappt super vielen Dank!!
Hast du eventuell eine Idee wie man die Daten von dem Farbsensor auf der LED-Matrix als Säulendiagramm darstellen kann? Mit den normalen Bausteinen hatte ich bis jetzt noch keinen wirklichen Erfolg…

1 „Gefällt mir“

Alle 3 Farben auf einmal oder der Reihe nach?

1 „Gefällt mir“

Annotation 2020-05-02 185427
Am besten alle 3 auf einmal wie auf dem Bild zu sehen. Sodass die erste Reihe Rot-, die dritte Grün- und die letze Blau-werte anzeigt. Das Diagramm soll sich dann kontinuierlich anpassen, damit die SchülerInnen live die Farbzusammensetzung von der Umgebung sehen können (soll denen halt die Funktionsweise eines Satelliten bzw. der Fernerkundung näher bringen).

1 „Gefällt mir“

Also nach langer Pause habe ich mich wieder dran gesetzt und bin zu diesem, wenn auch nicht sehr erfolgreichem Ergebnis, gekommen:


Benutzt habe ich diese Erweiterung: https://github.com/samnied/pxt-tcs34725
Leider war es mir mit dem Baustein Säulendiagramm nicht möglich alle drei RGB-Werte zeitgleich, nebeneinander auf dem Display zu visualisieren. Stattdessen laufen die einzelnen Säulendiagramme für jeden RGB-Wert nacheinander ab.
Hat jemand eine Idee wie man es hinbekommt, dass alles zeitgleich in einem Säulendiagramm dargestellt wird? Vielen lieben Dank im Voraus!! :slight_smile:
PS: habe bisher andere Möglichkeiten gesehen, die über Variablen und Listen gingen. Mit denen bin ich jedoch noch nicht sehr vertraut.

1 „Gefällt mir“

Vielleicht interessiert es noch andere. Für bis zu 5 Balken:



index
3 „Gefällt mir“

Hallo klmi,

klasse Arbeit hier im Forum!
Wie würdest Du einen einfache Übersetzung der RGB-Ergebnisse in die sechs Grundfarben umsetzen? Also z.B. bei 0,0,255 bis 20,20,230 → Blau usw.


Ich möchte eine Lego-Lok autonom fahren lassen und dazu auf die Schwellen entsprechende Fahrbefehle wie Halt, 50%, 100%, Pause etc. geben.
Das ist natürlich auch eine Frage der Lesegeschwindigkeit, aber erste Versuche sahen ganz vielversprechend aus.

LG Oskar

1 „Gefällt mir“