Zum Inhalt

matter_rgb.tc

Matter Smart Plug + RGB Color Light — pure TinyC on an ESP32-C6.

Source on GitHub

// Matter Smart Plug + RGB Color Light — pure TinyC on an ESP32-C6.
//
//   Endpoint 1: On/Off plug   -> relay 1   (set GPIO7 = Relay1 on the device)
//   Endpoint 2: Color Light    -> onboard WS2812 RGB LED on GPIO8 via rgbLed()
//
// The matter_c engine (firmware) does commissioning (SPAKE2+/PASE, CASE) and the
// Interaction Model. This script declares the two endpoints and turns Matter's
// On/Off + brightness (LevelControl) + colour (ColorControl Hue/Saturation) into
// an 0xRRGGBB value it pushes to the LED. Open pairing from the web /mt page
// (Bind), then control both devices from any Matter controller.

int plug_ep;     // relay endpoint id
int rgb_ep;      // colour-light endpoint id
int rgb_gpio;    // WS2812 data pin (onboard RGB on the C6 = GPIO8)
int rgb_on;      // light On/Off, tracked from the OnOff command

// HSV (each 0..254, the Matter range) -> packed 0xRRGGBB.
int hsv2rgb(int h, int s, int v) {
    int region; int rem; int p; int q; int t; int r; int g; int b;
    if (s == 0) { return (v << 16) + (v << 8) + v; }   // unsaturated -> white/grey
    region = (h * 6) / 255;
    rem    = (h * 6) - (region * 255);
    p = (v * (255 - s)) / 255;
    q = (v * (255 - (s * rem) / 255)) / 255;
    t = (v * (255 - (s * (255 - rem)) / 255)) / 255;
    r = v; g = t; b = p;                                // region 0
    if (region == 1) { r = q; g = v; b = p; }
    if (region == 2) { r = p; g = v; b = t; }
    if (region == 3) { r = p; g = q; b = v; }
    if (region == 4) { r = t; g = p; b = v; }
    if (region == 5) { r = v; g = p; b = q; }
    return (r << 16) + (g << 8) + b;
}

// Read the light's current Matter attributes and drive the LED.
void updateRGB() {
    int lvl; int hue; int sat;
    if (rgb_on == 0) { rgbLed(rgb_gpio, 0); return; }
    lvl = matterGet(rgb_ep, CLUSTER_LEVEL, 0);   // CurrentLevel 0..254 (brightness)
    hue = matterGet(rgb_ep, 0x0300, 0);          // ColorControl CurrentHue
    sat = matterGet(rgb_ep, 0x0300, 1);          // ColorControl CurrentSaturation
    if (lvl < 1) { lvl = 254; }                  // full brightness if unset
    rgbLed(rgb_gpio, hsv2rgb(hue, sat, lvl));
}

// A Matter controller invoked a command on one of our clusters. The engine has
// already applied LevelControl/ColorControl values to the attributes, so for the
// light we just re-read and refresh.
void MatterInvoke(int e, int cluster, int cmd) {
    if (e == plug_ep) {
        if (cluster == CLUSTER_ONOFF) {
            if (cmd == 2) { tasm_power = 1 - tasmPower(0); } else { tasm_power = cmd; }
        }
        return;
    }
    if (e == rgb_ep) {
        if (cluster == CLUSTER_ONOFF) {
            if (cmd == 2) { rgb_on = 1 - rgb_on; } else { rgb_on = cmd; }
        }
        updateRGB();
    }
}

int main() {
    rgb_gpio = 8;            // onboard WS2812 on the ESP32-C6 dev board
    rgb_on   = 0;

    matterReset();           // clean data model (root node only)

    // Endpoint 1 — On/Off plug -> relay 1 (GPIO7)
    plug_ep = matterAdd(MATTER_PLUG);
    matterName(plug_ep, "Plug");             // accessory title in the controller

    // Endpoint 2 — Extended Color Light (device type 0x010D) -> RGB on GPIO8
    rgb_ep = matterAdd(0x010D);
    matterName(rgb_ep, "RGB Light");         // accessory title in the controller
    matterCluster(rgb_ep, CLUSTER_ONOFF);
    matterAttr(rgb_ep, CLUSTER_ONOFF, 0, MTR_BOOL);   // OnOff
    matterCluster(rgb_ep, CLUSTER_LEVEL);
    matterAttr(rgb_ep, CLUSTER_LEVEL, 0, MTR_U8);     // CurrentLevel
    matterCluster(rgb_ep, 0x0300);                     // ColorControl
    matterAttr(rgb_ep, 0x0300, 0, MTR_U8);            // CurrentHue
    matterAttr(rgb_ep, 0x0300, 1, MTR_U8);            // CurrentSaturation
    matterSet(rgb_ep, CLUSTER_LEVEL, 0, 254);
    matterSet(rgb_ep, 0x0300, 0, 0);
    matterSet(rgb_ep, 0x0300, 1, 0);

    matterStart();           // Matter on (operational); press Bind on /mt to pair
    rgbLed(rgb_gpio, 0);     // LED off until paired + turned on
    return 0;
}