TrinketM0 as i2c NeoPixel Controller

Adafruit's tiny microcontroller platform. Please tell us which board you are using.
For CircuitPython issues, ask in the Adafruit CircuitPython forum.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
mcarpenterjr
 
Posts: 5
Joined: Tue Jan 01, 2019 2:35 pm

TrinketM0 as i2c NeoPixel Controller

Post by mcarpenterjr »

Hey all,

I have a project that I am working on which needs some fine lighting control. I determined the best tooling for the job would be a collection of (8) RGBW 8 Pixel Sticks and a Trinket M0, there is a jetson nano that will be issuing lighting commands to the trinket.

I think the best way to describe this would be the Jetson is the master and the Trinket is the slave? I'm new to i2c programming so please forgive my ignorance.

I was able to wire up the trinket and neopixels accordingly, I flashed some example code onto the trinket and the results are fantastic, everything lights up as expected, the lighting effects you can create are minded blowing and the color profiles are killer (it's my first time working with neoixels, kinda wish I did this sooner).

Where I begin to struggle is setting up the relationship between the trinket and the Jetson, if found this example code in the Pimironi Github; https://github.com/pimoroni/programming ... -neopixels, it's old and I'm having an issue flashing it to the trinket. But in addition to that, it looks like this only handles RGB neopixels and not RGBW. I'm not a C programmer, I write for the web daily, but I can see the patterns in use and I'm fairly confident through some trial and error I could extend the example code to work with RGBW.

Before I start re-inventing the wheel has anyone done this before, or at the very least know of an updated example, I might have missed in my endeavor here?

Any help is much appreciated.

User avatar
mcarpenterjr
 
Posts: 5
Joined: Tue Jan 01, 2019 2:35 pm

Re: TrinketM0 as i2c NeoPixel Controller

Post by mcarpenterjr »

So I made some updates to the provided example from the pimoroni GitHub repo;

Code: Select all

#include <Wire.h>
#include <Adafruit_NeoPixel.h>

// Open this header file to configure the NeoPixel i2c driver
#include "i2c-neopixels.h"

// Neo KHZ400 is necessary on the 8Mhz Trinket
// Note: NEO_KHZ400 has been made optional in recent versions of the NeoPixel library
// please open up Adafruit_NeoPixel.h and follow the instructions inside.
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXELS, PIN, PIXEL_COLOR_MODE + NEO_KHZ400);

// Tracks the current register pointer position and current pixel
// There are 3x as many "registers" as pixels, RGB*PIXELS
volatile uint8_t current_register = 0;
volatile uint8_t current_command = 0;
volatile uint8_t current_pixel = 0;

uint8_t r, g, b, w, x, n, o;

// Updates an individual color component according to the register position
void updatePixelColor(uint8_t value)
{
  current_pixel = (uint8_t)floor(current_register / 4) % (PIXELS*4);

  r = strip.getPixelColor( current_pixel ) >> 24;
  g = strip.getPixelColor( current_pixel ) >> 16;
  b = strip.getPixelColor( current_pixel ) >> 8;
  w = strip.getPixelColor( current_pixel );

  switch(current_register % 4) {
    case 0: // red
      strip.setPixelColor( current_pixel, value, g, b, w );
      break;
    case 1: // blue
      strip.setPixelColor( current_pixel, r, value, b, w );
      break;
    case 2: // green
      strip.setPixelColor( current_pixel, r, g, value, w );
      break;
    case 3: // white
      strip.setPixelColor( current_pixel, r, g, value );
      break;
  }
}

// Handles each Read request
void handleRead()
{  
  // Each pixel has 4 components, R, G, B and W, to get the pixel index divide by 4
  current_pixel = floor(current_register / 4);

  uint8_t component_color = (uint8_t)( strip.getPixelColor( current_pixel ) >> (24 - ((current_register % 4)*8)) );

  Wire.write(component_color);

  current_register = (current_register+1) % (PIXELS*4);
}

// Handles each Write request
void handleWrite(uint8_t NumBytes)
{
  // Exit if there is no data or more data than the buffer should ever allow
    if (NumBytes < 1 || NumBytes > TWI_RX_BUFFER_SIZE) return;

  // Always treat the first byte as the address
    current_register = Wire.read();
    NumBytes--;

  if( current_register == 255 && NumBytes )
  {
    handleCommand(NumBytes);
  }
  else
  {
    // If one byte, then addr set for next read
    if (!NumBytes) return;

    // Loop through the remaining bytes and update the pixel components sequentially
    while (NumBytes--)
    {
      updatePixelColor(Wire.read());
      current_register++;
    }
  }
  
  // Update the strip
    strip.show();
}

void handleCommand(uint8_t NumBytes)
{
  current_command = Wire.read();
  NumBytes--;
  switch(current_command){
    case CMD_ALL_OFF:
      for(x=0;x<PIXELS;x++){
        strip.setPixelColor(x,0,0,0,0);
      }
      break;
    case CMD_ALL_RGB:
      if (NumBytes < 4) return; // Not got R G B W components
      r = Wire.read();
      g = Wire.read();
      b = Wire.read();
      w = Wire.read();
          NumBytes-=4;

      for(x=0;x<PIXELS;x++){
        strip.setPixelColor(x,r,g,b,w);
      }
      break;
    case CMD_SOME_RGB:
      if (NumBytes < 5) return; // Not got R G B and W components
      r = Wire.read();
      g = Wire.read();
      b = Wire.read();
      w = Wire.read();
      n = Wire.read();
          NumBytes-=5;
  
      o = 0; // Starting pixel

      if(NumBytes)
      {
        o = Wire.read();
        NumBytes--;
      }

      for(x=0;x<PIXELS-o;x++){
        if(x%n == 0){
          strip.setPixelColor(x+o,r,g,b,w);
        }
      }
      break;
  }
  // Consume any leftover bytes
  while (NumBytes--)
  {
    Wire.read();
  }
}

void setup()
{
    Wire.begin(I2C_SLAVE_ADDRESS);
    Wire.onReceive(handleWrite);
    Wire.onRequest(handleRead);

    strip.begin();
    strip.show();
}

void loop()
{
    // Wire uses delay in the loop
    delay(100);
}
I swapped the TinyWireS library for Wire, which I believe should work...? I'm not sure what the diffs are between the 2 but Wire seemed to have similar functions. I haven't been able to flash the code to the trinket since I am now up against an issue with the `handleWrite` function, it get the complaint `invalid conversion from 'void (*) (uint8_t)'` I know C++ is a typed language, I'm just not sure why it would complain in this case, I can see void is being cast upon the function itself but that shouldn't affect what's being passed..?
I could be totally wrong here, again I'm not a C guy I do PHP, node and python.

User avatar
mcarpenterjr
 
Posts: 5
Joined: Tue Jan 01, 2019 2:35 pm

Re: TrinketM0 as i2c NeoPixel Controller

Post by mcarpenterjr »

So I managed to get this working, I just used int instead of uint_8. Attaching the finalized stuff for later reference.

controller.ino -

Code: Select all

#include <Wire.h>
#include <Adafruit_NeoPixel.h>

// Open this header file to configure the NeoPixel i2c driver
#include "i2c-neopixels.h"

// Neo KHZ400 is necessary on the 8Mhz Trinket
// Note: NEO_KHZ400 has been made optional in recent versions of the NeoPixel library
// please open up Adafruit_NeoPixel.h and follow the instructions inside.
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXELS, PIN, PIXEL_COLOR_MODE + NEO_KHZ400);

// Tracks the current register pointer position and current pixel
// There are 3x as many "registers" as pixels, RGB*PIXELS
volatile uint8_t current_register = 0;
volatile uint8_t current_command = 0;
volatile uint8_t current_pixel = 0;

uint8_t r, g, b, w, x, n, o;

// Updates an individual color component according to the register position
void updatePixelColor(uint8_t value)
{
  current_pixel = (uint8_t)floor(current_register / 4) % (PIXELS*4);

  r = strip.getPixelColor( current_pixel ) >> 24;
  g = strip.getPixelColor( current_pixel ) >> 16;
  b = strip.getPixelColor( current_pixel ) >> 8;
  w = strip.getPixelColor( current_pixel );

  switch(current_register % 4) {
    case 0: // red
      strip.setPixelColor( current_pixel, value, g, b, w );
      break;
    case 1: // blue
      strip.setPixelColor( current_pixel, r, value, b, w );
      break;
    case 2: // green
      strip.setPixelColor( current_pixel, r, g, value, w );
      break;
    case 3: // white
      strip.setPixelColor( current_pixel, r, g, value );
      break;
  }
}

// Handles each Read request
void handleRead()
{  
  // Each pixel has 4 components, R, G, B and W, to get the pixel index divide by 4
  current_pixel = floor(current_register / 4);

  uint8_t component_color = (uint8_t)( strip.getPixelColor( current_pixel ) >> (24 - ((current_register % 4)*8)) );

  Wire.write(component_color);

  current_register = (current_register+1) % (PIXELS*4);
}

// Handles each Write request
void handleWrite(int NumBytes)
{
  // Exit if there is no data or more data than the buffer should ever allow
    if (NumBytes < 1 || NumBytes > TWI_RX_BUFFER_SIZE) return;

  // Always treat the first byte as the address
    current_register = Wire.read();
    NumBytes--;

  if( current_register == 255 && NumBytes )
  {
    handleCommand(NumBytes);
  }
  else
  {
    // If one byte, then addr set for next read
    if (!NumBytes) return;

    // Loop through the remaining bytes and update the pixel components sequentially
    while (NumBytes--)
    {
      updatePixelColor(Wire.read());
      current_register++;
    }
  }
  
  // Update the strip
    strip.show();
}

void handleCommand(uint8_t NumBytes)
{
  current_command = Wire.read();
  NumBytes--;
  switch(current_command){
    case CMD_ALL_OFF:
      for(x=0;x<PIXELS;x++){
        strip.setPixelColor(x,0,0,0,0);
      }
      break;
    case CMD_ALL_RGB:
      if (NumBytes < 4) return; // Not got R G B W components
      r = Wire.read();
      g = Wire.read();
      b = Wire.read();
      w = Wire.read();
          NumBytes-=4;

      for(x=0;x<PIXELS;x++){
        strip.setPixelColor(x,r,g,b,w);
      }
      break;
    case CMD_SOME_RGB:
      if (NumBytes < 5) return; // Not got R G B and W components
      r = Wire.read();
      g = Wire.read();
      b = Wire.read();
      w = Wire.read();
      n = Wire.read();
          NumBytes-=5;
  
      o = 0; // Starting pixel

      if(NumBytes)
      {
        o = Wire.read();
        NumBytes--;
      }

      for(x=0;x<PIXELS-o;x++){
        if(x%n == 0){
          strip.setPixelColor(x+o,r,g,b,w);
        }
      }
      break;
  }
  // Consume any leftover bytes
  while (NumBytes--)
  {
    Wire.read();
  }
}

void setup()
{
    Wire.begin(I2C_SLAVE_ADDRESS);
    Wire.onReceive(handleWrite);
    Wire.onRequest(handleRead);

    strip.begin();
    strip.show();
}

void loop()
{
    // Wire uses delay in the loop
    delay(100);
}
i2c-neopixels.h

Code: Select all

/************************
* VALUES YOU CAN CHANGE *
*************************/

// The I2C address that your Trinket will use
#define I2C_SLAVE_ADDRESS 0x4

// NeoPixel PIN, and pixel count
// Brightness MUST be 255 or colours can't be read back correctly from the strip
#define PIN 1 // Pin the strip is connected to
#define PIXELS 16 // Maximum of 85 pixels
#define PIXEL_COLOR_MODE NEO_GRBW // You might want to change this to NEO_RGB if you're using anything but real NeoPixels

/************************
*    EVERYTHING ELSE    *
*************************/
// Only change this if you have modified in TinyWireS
#ifndef TWI_RX_BUFFER_SIZE
#define TWI_RX_BUFFER_SIZE ( 24 )
#endif

#define __AVR_ATtiny85_

// These define the commands available when writing via i2c
#define CMD_ALL_OFF 0x00
#define CMD_ALL_RGB 0x01
#define CMD_SOME_RGB 0x02

void updatePixelColor(uint8_t value);
void handleRead();
void handleWrite(uint8_t NumBytes);
void handleCommand(uint8_t NumBytes);
void setup();
void loop();

Locked
Please be positive and constructive with your questions and comments.

Return to “Trinket ATTiny, Trinket M0”