Welcome, guest! Login / Register - Why register?
Psst.. new poll here.
[email protected] webmail now available. Want one? Go here.
Cannot use outlook/hotmail/live here to register as they blocking our mail servers. #microsoftdeez
Obey the Epel!

Paste

Pasted as C by jke ( 5 years ago )
/**
 * Digital Fireplace Firmware v0.1
 * 24/12/2018
 *
 * By: Boldizsar Bednarik
 * Email: [email protected]
 *
 * Terms Of Use:
 *
 * You're free to use and modify for non commercial purposes
 * If you want to use in a commercial project, please contact me
 *
 */

#include <Arduino.h>
#include <FastLED.h>

#define LED_PIN 5
#define BUTTON_PIN 4
#define DEBOUNCE_TIME 500
#define BRIGHTNESS 128
#define LED_TYPE WS2811
#define COLOR_ORDER GRB

#define DELAY_PER_CYCLE 15

#define MIN_ADD_PARTICLE_CYCLE 1
#define MAX_ADD_PARTICLE_CYCLE 2

#define MIN_BRIGHTNESS 40
#define MAX_BRIGHTNESS 240

#define MIN_POSTPROCESS_CYCLE 2
#define MAX_POSTPROCESS_CYCLE 4

#define WIDTH 7
#define HEIGHT 16
#define NUM_LEDS WIDTH *HEIGHT
#define DO_MASK true

#define BRIBHTNESS_CHANGE_STEP 10

#define COLOR_MAX_VALUE 255

int _actualAddParticles = MAX_ADD_PARTICLE_CYCLE;
int _actualMaxBrightness = MAX_BRIGHTNESS;

/**
 * Mask the fire to give it a shape,
 * applied in post processing
 */

float masking[HEIGHT][WIDTH] = {
    {0.1, 0.2, 0.4, 1.0, 0.4, 0.2, 0.1},
    {0.1, 0.3, 0.5, 1.0, 0.5, 0.3, 0.1},
    {0.2, 0.4, 0.7, 1.0, 0.7, 0.4, 0.2},
    {0.2, 0.5, 1.0, 1.0, 1.0, 0.5, 0.2},
    {0.3, 0.7, 1.0, 1.0, 1.0, 0.7, 0.3},
    {0.3, 0.9, 1.0, 1.0, 1.0, 0.9, 0.3},
    {0.7, 1.0, 1.0, 1.0, 1.0, 1.0, 0.7},
    {0.8, 1.0, 1.0, 1.0, 1.0, 1.0, 0.8},
    {0.9, 1.0, 1.0, 1.0, 1.0, 1.0, 0.9},
    {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0},
    {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0},
    {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0},
    {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0},
    {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0},
    {0.4, 1.0, 1.0, 1.0, 1.0, 1.0, 0.4},
    {0.2, 0.5, 1.0, 1.0, 1.0, 0.5, 0.2}};

/**
 * Mask the bottom of the fire to give it a more realistic shape
 * applied in post processing
 */

float bottomMask[][WIDTH] = {
    {0.0, 0.2, 0.0, 0.5, 0.0, 0.2, 0.0},
    {0.5, 0.5, 1.0, 1.0, 1.0, 0.5, 0.5},
    {0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5},
    {0.2, 0.5, 1.0, 1.0, 1.0, 0.5, 0.2}};

/**
 * Placeholder for bottom part calculation which is applied post
 * fire calculation
 */
int _postProcess[][WIDTH][3] = {
    {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
    {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
    {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
    {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}};

/**
 * Matrix used for convolution
 */
float convolutionMatrix[3][3] = {
    {0.0, 0.0, 0},
    {0, 0.7, 0},
    {0.2, 1, 0.2}};

/**
 * Minimum divider for convolution
 */
#define MIN_CONVOLUTINO_DEVIDER 2.09

/**
 * Maximum divider for convolution
 */
#define MAX_CONVOLUTINO_DEVIDER 2.3

float convolutionDivider = MIN_CONVOLUTINO_DEVIDER;

int ACTIVE_PALETTE_INDEX = 0;

int palettes[][3][3] = {
    // realistic fire 1
    {
        {207, 116, 14},
        {212, 152, 48},
        {140, 48, 8}},
    // green fire
    {
        {50, 80, 22},
        {111, 121, 21},
        {30, 60, 30}},
    // realistic fire 2
    {
        {218, 51, 0},
        {100, 55, 0},
        {254, 200, 0}},
    // funky
    {
        {0, 0, 255},
        {0, 255, 0},
        {255, 0, 0}},
    // aqua fire
    {
        {119, 194, 193},
        {62, 172, 236},
        {23, 41, 105}},
    // x-mass
    {
        {125, 125, 125},
        {0, 255, 0},
        {255, 0, 0}}};

/**
 * display and buffer martix definitions
 */
int display[WIDTH][HEIGHT][3];
int buffer[WIDTH][HEIGHT][3];

/**
 * variables to control the fire intensity
 */
int particleAddedCnt = MAX_ADD_PARTICLE_CYCLE;
int postProcessCycle = MAX_POSTPROCESS_CYCLE;
int postProcessCnt = MAX_POSTPROCESS_CYCLE;
int maxParticlesPerCycle = MAX_ADD_PARTICLE_CYCLE;

CRGB leds[NUM_LEDS];

/**
 * set initial colors for display matrix container array
 */
void initializeDisplayMatrix()
{
  for (int i = 0; i < WIDTH; i++)
  {
    for (int j = 0; j < HEIGHT; j++)
    {
      for (int p = 0; p < 3; p++)
      {
        display[i][j][p] = 0;
        buffer[i][j][p] = 0;
      }
    }
  }
}

/**
 * Copy buffer value to display value
 */
void copyBufferToDisplay()
{
  for (int i = 0; i < WIDTH; i++)
  {
    for (int j = 0; j < HEIGHT; j++)
    {
      for (int p = 0; p < 3; p++)
      {
        display[i][j][p] = buffer[i][j][p];
      }
    }
  }
}

/**
 * Add new particle to the display array
 */
void addNewParticle()
{
  particleAddedCnt++;

  if (particleAddedCnt >= _actualAddParticles)
  {
    particleAddedCnt = 0;
    int rx = rand() % WIDTH;
    int ry = ceil(5 * HEIGHT / 6 + rand() % HEIGHT / 6);

    int _ct = rand() % 3;
    int _rc = 1 + rand() % 6;

    for (int p = 0; p < 3; p++)
    {
      display[rx][ry][p] = (int)((float)palettes[ACTIVE_PALETTE_INDEX][_ct][p] / (float)_rc);
    }
  }
}

int _lastPressed = 0;

/**
 * INTERRUPT
 * Change the fire color palette when button is pressed
 */
void changeMode()
{
  if (millis() - _lastPressed > DEBOUNCE_TIME)
  {
    ACTIVE_PALETTE_INDEX++;

    if (ACTIVE_PALETTE_INDEX >= sizeof(palettes) / sizeof(*palettes))
    {
      ACTIVE_PALETTE_INDEX = 0;
    }
    initializeDisplayMatrix();
    _lastPressed = millis();
  }
}

/**
 * Setup function
 */
void setup()
{
  delay(500);
  Serial.begin(115200);
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.clear();

  FastLED.setBrightness(BRIGHTNESS);

  // setup values
  initializeDisplayMatrix();

  // set button pin
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), changeMode, FALLING);
}

/**
 * Transform coordinates to index and set let
 */
void setLedByCoord(int x, int y, int _color[3])
{
  int idy = ((x % 2 == 0) ? HEIGHT - y - 1 : y);
  int index = (x * HEIGHT) + idy;

  int _cr = _color[0];
  int _cg = _color[1];
  int _cb = _color[2];

  // apply mask

  if (DO_MASK)
  {
    _cr = (int)((float)_cr * (float)masking[y][x]);
    _cg = (int)((float)_cg * (float)masking[y][x]);
    _cb = (int)((float)_cb * (float)masking[y][x]);
  }

  leds[index].red = _cr;
  leds[index].green = _cg;
  leds[index].blue = _cb;
}

/**
 * post process
 */
void postProcessCalculate()
{
  // add glowing to the botton with masking

  for (int i = 0; i < WIDTH; i++)
  {
    for (int j = 0; j < sizeof(bottomMask) / sizeof(*bottomMask); j++)
    {
      if (bottomMask[j][i] > 0.0)
      {
        int _color[3];
        int _ct = rand() % 3;
        int _rc = 10 + rand() % 10;

        for (int p = 0; p < 3; p++)
        {
          _postProcess[j][i][p] = (int)(((float)palettes[ACTIVE_PALETTE_INDEX][_ct][p] / (float)_rc) * (float)bottomMask[j][i]);
        }
      }
    }
  }
}

float _brightness = (_actualMaxBrightness - MIN_BRIGHTNESS) / 2;
float _brigntnessSpeedChange = BRIBHTNESS_CHANGE_STEP;

/**
 * Change fire brightness periodically
 */
void changeBrightness()
{
  // brightness change over time

  // pingpong effect

  if (_brightness > _actualMaxBrightness)
  {
    _brightness = (float)_actualMaxBrightness;
    _brigntnessSpeedChange = -BRIBHTNESS_CHANGE_STEP;
  }

  if (_brightness < MIN_BRIGHTNESS)
  {
    _brightness = (float)MIN_BRIGHTNESS;
    _brigntnessSpeedChange = BRIBHTNESS_CHANGE_STEP;
  }

  // or reverse randomly

  if (rand() % 100 < 10)
  {
    _brigntnessSpeedChange = -_brigntnessSpeedChange;
  }

  // change brightness

  _brightness += _brigntnessSpeedChange;

  FastLED.setBrightness((int)_brightness);
}

/**
 * Add post process stuff here
 */
void postProcess()
{
  postProcessCnt++;

  if (postProcessCnt >= postProcessCycle)
  {
    postProcessCnt = 0;
    postProcessCalculate();
    changeBrightness();
  }

  for (int i = 0; i < WIDTH; i++)
  {
    for (int j = 0; j < 4; j++)
    {
      if(bottomMask[j][i] > 0)
        {
          setLedByCoord(i, HEIGHT - 4 + j, _postProcess[j][i]);
        }
    }
  }
}

/**
 * show data on led strip from display value
 */
void show()
{
  for (int i = 0; i < WIDTH; i++)
  {
    for (int j = 0; j < HEIGHT; j++)
    {
      setLedByCoord(i, j, display[i][j]);
    }
  }

  postProcess();

  FastLED.show();
}

/**
 * Get pixel data safely ( if there's no -1 or +1 pixel, return the closest one)
 */
int getSafePixelData(int x, int y, int colorIndex)
{
  x = x < 0 ? 0 : x;
  x = x > WIDTH - 1 ? WIDTH - 1 : x;

  y = y < 0 ? 0 : y;
  y = y > HEIGHT - 1 ? HEIGHT - 1 : y;

  return display[x][y][colorIndex];
}

/**
 * calculate pixel value for convolution
 */
void claculatePixel(int x, int y)
{
  float _newPixel[] = {0, 0, 0};

  for (int i = -1; i < 2; i++)
  {
    for (int j = -1; j < 2; j++)
    {
      float _pixel[3] = {
          getSafePixelData(x + i, y + j, 0),
          getSafePixelData(x + i, y + j, 1),
          getSafePixelData(x + i, y + j, 2)};

      for (int p = 0; p < 3; p++)
      {
        _newPixel[p] = _newPixel[p] + (int)((float)_pixel[p] * (float)convolutionMatrix[j + 1][i + 1]);
      }
    }
  }

  for (int p = 0; p < 3; p++)
  {
    buffer[x][y][p] = (int)min(COLOR_MAX_VALUE, (int)((float)_newPixel[p] / (float)convolutionDivider));
  }
}

/**
 * Do the interpolation to all pixels
 */
void interpolate()
{

  for (int i = 0; i < WIDTH; i++)
  {
    for (int j = 0; j < HEIGHT; j++)
    {
      claculatePixel(i, j);
    }
  }

  copyBufferToDisplay();
}

int _lastAnalogValue = 0;

/**
 * get analog value, and apply fire intensity changes
 */
void adjustFireIntensity()
{
  int _a = analogRead(A0);
  bool _didAnalogChanged = abs(_a - _lastAnalogValue) > 10;
  _lastAnalogValue = _a;

  if (_didAnalogChanged)
  {
    _brightness = MAX_BRIGHTNESS;
  }

  // calc min brightness between MIN_BRIGHTNESS and MIN_BRIGHTNESS+50
  int _minBrightness = MIN_BRIGHTNESS + (int)((float)_a / 20);

  float _proportion = (float)_a / 1000;

  _actualMaxBrightness = _minBrightness + (int)((float)(MAX_BRIGHTNESS - _minBrightness) * _proportion);
  convolutionDivider = MAX_CONVOLUTINO_DEVIDER - (float)((float)(MAX_CONVOLUTINO_DEVIDER - MIN_CONVOLUTINO_DEVIDER) * _proportion);
  _actualAddParticles = MAX_ADD_PARTICLE_CYCLE - (int)((float)(MAX_ADD_PARTICLE_CYCLE - MIN_ADD_PARTICLE_CYCLE) * _proportion);
  postProcessCycle = MAX_POSTPROCESS_CYCLE - (int)((float)(MAX_POSTPROCESS_CYCLE - MIN_POSTPROCESS_CYCLE) * _proportion);
}

/**
 * main loop
 */
void loop()
{
  adjustFireIntensity();
  addNewParticle();
  interpolate();
  show();
  delay(DELAY_PER_CYCLE);
}

 

Revise this Paste

Your Name: Code Language: