theaccordionist
Published © GPL3+

Ironman

Programmable, circular, wearable LED array.

AdvancedFull instructions provided2,593
Ironman

Things used in this project

Hardware components

IC & Component Socket, 8 Contacts
IC & Component Socket, 8 Contacts
Not actually that one, but a 16-bit IO expander controlled via I2C that fits the footprint. Check the schematic
×1
LED (generic)
LED (generic)
SMD 0603, 32x
×1
Resistor 1M ohm
Resistor 1M ohm
Not actually this one. 0402 SMD sized according to the schematic. For the undocumented I2C PUs, you can go 1k
×1
Arduino Nano R3
Arduino Nano R3
I probably used a knockoff from uncle Bezos
×1
Female Header 8 Position 1 Row (0.1")
Female Header 8 Position 1 Row (0.1")
0.1" but sized to the Nano's pin count
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Schematic

Schematic and PCB data in attached zip. EagleCAD

Code

Arduino sketch

Arduino
Software implementation, Arduino IDE.
/*
 *  Program for Ironman logo
 *  By: JJ
 */

#include <Wire.h>

unsigned char button_id = 12;
bool button_press = true;
unsigned long int debouncer = 0;
char pattern_num = 5;

int alternateFlag = 0;

unsigned char pattern_buffer[32][32];
unsigned char ledstate[38];
unsigned char sawtooth_poly[32];
unsigned int pwm_pin_nums[6];
unsigned char sawtooth = 0;
unsigned long int triangle = 0;
bool triangle_up = true;

char buffer_index = 0;

unsigned long int timeTracker;

// the setup function runs once when you press reset or power the board
void setup() {
   
  // Polynomial delinearization via the following formula
  //     ((x^0.5) * 32^0.5) + 1
  // This will allow us to linearize the apparent brightness of the LEDs
  // We will use a lookup table to improve runtime efficiency
  float sqrt_32 = sqrt(32);
  for (int i = 0; i < 32; i++)
  {
    //sawtooth_poly[i] = ( i * i / 32 ) + 1;
    sawtooth_poly[i] = (sqrt(i) * sqrt_32 + 1) * 8;
  }
  
  for (int i = 0; i < 38; i++)
  {
    ledstate[i] = 0;
  }
  
  int ledPin1 = 3;  
  int ledPin2 = 5; 
  int ledPin3 = 6;   
  int ledPin4 = 9;  
  int ledPin5 = 10;  
  int ledPin6 = 11;   
  
  pwm_pin_nums[0] = ledPin1;
  pwm_pin_nums[1] = ledPin2;
  pwm_pin_nums[2] = ledPin3;
  pwm_pin_nums[3] = ledPin4;
  pwm_pin_nums[4] = ledPin5;
  pwm_pin_nums[5] = ledPin6;
  
  
  // initialize digital pins as outputs
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  pinMode(ledPin4, OUTPUT);
  pinMode(ledPin5, OUTPUT);
  pinMode(ledPin6, OUTPUT);
  
  pinMode(button_id, INPUT_PULLUP);

  //timeTracker = millis();

  // Open up the floodgates of I2C
  Wire.begin(); // join i2c bus (address optional for master)

  // Change the I2C clock rate from 100k to 400k by knocking the TWBR value down from 72 to 18
  TWBR = 18;
  
  Wire.beginTransmission(0x20); // Select the first chip
  Wire.write(0x06);             // Enter command to configure this bubby
  Wire.write(0x00);             // Enable outputs on P0
  Wire.write(0x00);             // Enable outputs on P1
  Wire.endTransmission();       // Finish up the transaction
  
  Wire.beginTransmission(0x21); // Select the first chip
  Wire.write(0x06);             // Enter command to configure this bubby
  Wire.write(0x00);             // Enable outputs on P0
  Wire.write(0x00);             // Enable outputs on P1
  Wire.endTransmission();       // Finish up the transaction  
}

// the loop function runs over and over again forever
void loop() 
{
  if (button_press)
  {
    buffer_index = 0;
    button_press = false; 
    generate_new_pattern();
  }
  
  // If we got a button press, do some rudimentary debouncing and set a flag for later
  if (!digitalRead(button_id))
  {
    if (debouncer < millis() && !button_press)
    {
       debouncer = millis() + 1000;
       button_press = true;
    }
  }
  calculate_values();

  write_values();
}

void generate_new_pattern()
{
  switch (++pattern_num)
  {
    case 1:
        for (int led_iter = 0; led_iter < 32; led_iter++)
        {
            for (int buffer_iter = 0; buffer_iter < 32; buffer_iter++)
            {
               pattern_buffer[led_iter][buffer_iter] = max( ( (32 - ((led_iter + buffer_iter) % 32)) << 3) - 64, 0);
            }
        }
        break;
    case 2:
        for (int led_iter = 0; led_iter < 32; led_iter++)
        {
            for (int buffer_iter = 0; buffer_iter < 32; buffer_iter++)
            {
               pattern_buffer[led_iter][buffer_iter] = max((abs(16 - buffer_iter) << 3) - 33, 0); ;
            }
        }
        break;
    case 3:      
      for (int led_iter = 0; led_iter < 32; led_iter++)
      {
          for (int buffer_iter = 0; buffer_iter < 32; buffer_iter++)
          {
              if (buffer_iter == 0) pattern_buffer[led_iter][buffer_iter] = (rand() % 50) + 15;
              else pattern_buffer[led_iter][buffer_iter] = min(max(pattern_buffer[led_iter][buffer_iter - 1] + ((rand() % 3) - 1) * 10, 0), 255);
          }
      }
      break;
    case 4:
      for (int led_iter = 0; led_iter < 32; led_iter++)
      {
          for (int buffer_iter = 0; buffer_iter < 32; buffer_iter++)
          {
              pattern_buffer[led_iter][buffer_iter] = max( (((led_iter % 16 + (32 - buffer_iter)) % 16) << 5) - 256, 0);
          }
      }
      break;
    case 5:
      for (int led_iter = 0; led_iter < 32; led_iter++)
      {
          int peaks = 3;
          for (int buffer_iter = 0; buffer_iter < 32; buffer_iter++)
          {
              pattern_buffer[led_iter][buffer_iter] = min(max(300 * (sin(2 * PI * peaks * (buffer_iter + led_iter) / 32.0) + 1) - 400, 0), 255);
          }
      }
      break;
    case 6:
      for (int led_iter = 0; led_iter < 32; led_iter++)
      {
          // Left to right. Convert LED number into an angle (radians)
          float led_pos_x = cos(((led_iter - 8) % 32) / 32.0 * 2 * PI);
          float led_pos_y = sin(((led_iter - 8) % 32) / 32.0 * 2 * PI);
          float led_pos_xn = cos(((led_iter + 8) % 32) / 32.0 * 2 * PI);
          float led_pos_yn = sin(((led_iter + 8) % 32) / 32.0 * 2 * PI);
          float width = 0.2;
          for (int buffer_iter = 0; buffer_iter < 32; buffer_iter++)
          {
              // Convert our buffer position into a horizontal displacement
              float buffer_pos = (buffer_iter % 8) / 3.0 - 1.25;

              if (buffer_iter < 8)
              {
                float horiz_dist = abs(buffer_pos - led_pos_x);
  
                if (horiz_dist < width)
                {
                    pattern_buffer[led_iter][buffer_iter] = (width - horiz_dist) / width * 255;
                }
                else
                {
                    pattern_buffer[led_iter][buffer_iter] = 0;
                }
              }
              else if (buffer_iter < 16)
              {
                float vert_dist = abs(buffer_pos - led_pos_y);
  
                if (vert_dist < width)
                {
                    pattern_buffer[led_iter][buffer_iter] = (width - vert_dist) / width * 255;
                }
                else
                {
                    pattern_buffer[led_iter][buffer_iter] = 0;
                }
              }
              else if (buffer_iter < 24)
              {
                float horiz_dist = abs(buffer_pos - led_pos_xn);
  
                if (horiz_dist < width)
                {
                    pattern_buffer[led_iter][buffer_iter] = (width - horiz_dist) / width * 255;
                }
                else
                {
                    pattern_buffer[led_iter][buffer_iter] = 0;
                }
              }
              else
              {
                float vert_dist = abs(buffer_pos - led_pos_yn);
  
                if (vert_dist < width)
                {
                    pattern_buffer[led_iter][buffer_iter] = (width - vert_dist) / width * 255;
                }
                else
                {
                    pattern_buffer[led_iter][buffer_iter] = 0;
                }
              }
              
          }
      }
      break;
    default:
      for (int led_iter = 0; led_iter < 32; led_iter++)
      {
          for (int buffer_iter = 0; buffer_iter < 32; buffer_iter++)
          {
             unsigned char cw  = max( ( (32 - ((led_iter + buffer_iter) % 32)) << 5) - 768, 0);
             unsigned char ccw = max( ( (((led_iter + 32 - buffer_iter) % 32)) << 5) - 768, 0);
             
             pattern_buffer[led_iter][buffer_iter] = max(cw, ccw);
          }
      }
      pattern_num = 0;
      break;
  }
}

void calculate_values()
{
  sawtooth += 8;
  
  if (timeTracker <= millis())
  {
     buffer_index++;
     if (buffer_index >= 32)
     {
        buffer_index %= 32;
     } 
     timeTracker += 50;
  }

  for (int i = 0; i < 32; i++)
  {
    ledstate[i] = pattern_buffer[i][buffer_index];
  }  
  
  for ( int i = 32; i < 38; i ++)
  {
     ledstate[i] = max((abs(16 - buffer_index) << 4) - 33, 0); 
  }
}


void write_values()
{ 
  unsigned char byte1 = 0;
  unsigned char byte2 = 0;
  unsigned char byte3 = 0;
  unsigned char byte4 = 0;
  
  for (int iter = 0; iter < 8; iter++)
  {
    if (ledstate[iter] <= sawtooth_poly[sawtooth >> 3])
    {
      byte1 = byte1 | (1 << (iter % 8));
    }
  }

  for (int iter = 8; iter < 16; iter++)
  {
    if (ledstate[iter] <= sawtooth_poly[sawtooth >> 3])
    {
      byte2 = byte2 | (1 << (iter % 8));
    }
  }
  for (int iter = 16; iter < 24; iter++)
  {
    if (ledstate[iter] <= sawtooth_poly[sawtooth >> 3])
    {
      byte3 = byte3 | (1 << (iter % 8));
    }
  }
  for (int iter = 24; iter < 32; iter++)
  {
    if (ledstate[iter] <= sawtooth_poly[sawtooth >> 3])
    {
      byte4 = byte4 | (1 << (iter % 8));
    }
  }
  
  Wire.beginTransmission(0x20); // Select the first chip
  Wire.write(0x02);             // Enter command to configure this bubby
  Wire.write(byte1);     // Enable outputs on P0
  Wire.write(byte2);     // Enable outputs on P1
  Wire.endTransmission();       // Finish up the transaction  
  
  Wire.beginTransmission(0x21); // Select the first chip
  Wire.write(0x02);             // Enter command to configure this bubby
  Wire.write(byte3);     // Enable outputs on P0
  Wire.write(byte4);     // Enable outputs on P1
  Wire.endTransmission();       // Finish up the transaction  
  
  for (int i = 32; i < 38; i++)
  {
    analogWrite(pwm_pin_nums[i - 32], ledstate[i]);  
  }

}

Credits

theaccordionist
1 project • 3 followers

Comments