Saeid Moghadam
Published © MIT

RANGMANG RGB controller

RANGMANG is an RGB controller based on the ATmega128 ATmega328p,STM32F030K6T6 microcontroller, Programmed with serial communication.

IntermediateWork in progress2 hours285
RANGMANG RGB controller

Things used in this project

Story

Read more

Code

Rangmang_Arduino.ino

Arduino
Arduino C++ firmware for RANGMANG RGB Controller (ATmega328P) using FastLED library. Equivalent to the original BASCOM implementation.
/*
 * Rangmang Controller - Arduino port (revised)
 * Original: Saeid Moghadam (Bascom AVR, ATmega328P)
 * Hardware: ATmega328P, WS2812 on D5, serial 9600
 *
 * Protocol:  A(adr) D(mode) R G B S E M      e.g. A1D0R255G0B0S0E4M0
 *            CH<old>=<new>                   address change -> "New Address=<new>"
 */

#include <FastLED.h>
#include <EEPROM.h>

#define LED_PIN      5
#define NUM_LEDS     14        // number of pixels on your strip
#define COLOR_ORDER  GRB       // WS2812B = GRB, some WS2811 = RBG
#define DEBUG        0         // 1 = print parsed values (do NOT use on a shared bus)

// Rb_shiftleft/right in Bascom: vacated pixel black (0) or current color (1)?
// Verify on hardware against the Bascom build (see notes).
#define SHIFT_FILL_COLOR 0

CRGB leds[NUM_LEDS];

byte  Ch0 = 1, Dan = 0, Danc = 0, Dans = 0;
byte  Rr = 255, Gg = 255, Bb = 255;
byte  Rx = 255, Gx = 255, Bx = 255;
word  Led = NUM_LEDS - 1;
word  Tim1 = 30, Tim2 = 30;
word  Ns = 0, Nf = NUM_LEDS - 1;
byte  Mx = 0, Nnb = 0, Chc = 0;
word  Ado = 0, T = 0;
bool  Rainbo = false;
volatile bool Ina = false;

char  Text[41];
byte  TextLen = 0;

#define ABORT  if (Ina) return

// ---------------- prototypes ----------------
void Chekm(); void Time1(); void Time2(); void Modh(); void Red();
void Snak_run_slow(); void Snak_return_slow(); void Snak_run_fast(); void Snak_return_fast();
void Snak_run_slow_random(); void Snak_return_slow_random();
void Snak_run_fast_random(); void Snak_return_fast_random();
void Snak_runreturn_slow_random(); void Half_snak_out_mid_slow();
void Half_half_right_slow(); void Half_half_left_slow();
void Star_runreturn_slow(); void Star_run_slow(); void Star_run_fast(); void Star_return_fast();
void Snak_growup_slow(); void Star_playful(); void Snak_star_playful(); void Star_fillers();
void Movable_light(); void Snak_runreturn_colored();
void Star_diamonds_blink_slow(); void Star_diamonds_blink_fast(); void Star_diamonds_blink_faster();
void Disco(); void Fillstripe_color(); void Breathing();
void Percolate_run(); void Percolate_return();
void Snak_1_2_run(); void Snak_1_2_return(); void Snak_1_2_run_rev(); void Snak_1_2_return_rev();
void Snak_relax_run(); void Snak_relax_return();
void Snak_rainbow(); void Snak_rainbow_lp(); void Snak_rainbow_bl();

// ---------------- helpers ----------------
// Bascom Rnd(n) returns 0..n-1
static word rnd(word n) { return n ? (word)random(n) : 0; }
static CRGB rndCol()    { return CRGB(rnd(Rx), rnd(Gx), rnd(Bx)); }
static void setPx(int i, CRGB c) { if (i >= 0 && i < NUM_LEDS) leds[i] = c; }
static CRGB fillCol() { return SHIFT_FILL_COLOR ? CRGB(Rx, Gx, Bx) : CRGB(0, 0, 0); }

static void shR() {                      // shift right over 0..Led
  for (int i = Led; i > 0; i--) leds[i] = leds[i - 1];
  leds[0] = fillCol();
}
static void shL() {                      // shift left over 0..Led
  for (int i = 0; i < (int)Led; i++) leds[i] = leds[i + 1];
  leds[Led] = fillCol();
}
static void rotR() {
  CRGB last = leds[Led];
  for (int i = Led; i > 0; i--) leds[i] = leds[i - 1];
  leds[0] = last;
}
static void rotL() {
  CRGB first = leds[0];
  for (int i = 0; i < (int)Led; i++) leds[i] = leds[i + 1];
  leds[Led] = first;
}

static int num(const char* s, byte maxDigits) {
  int v = 0;
  while (maxDigits-- && *s >= '0' && *s <= '9') { v = v * 10 + (*s - '0'); s++; }
  return v;
}

// ---------------- EEPROM (same header addresses as Bascom) ----------------
// 1:Ch0  2:Dan  3,4,5:R,G,B  6-7:Led  9:Memoclr  10-11:Tim1  12-13:Tim2
// slot I (1..98): R@100+I  G@200+I  B@300+I  start@396+4I  end@398+4I   (I = 99 - M)
static word eeGetW(int a) { return (word)EEPROM.read(a) | ((word)EEPROM.read(a + 1) << 8); }
static void eePutW(int a, word v) { EEPROM.update(a, v & 0xFF); EEPROM.update(a + 1, v >> 8); }

static void Memclr() {
  Serial.println(F("   "));
  Serial.println(F(" Rangmang Wait "));
  for (int i = 0; i <= 800; i++) EEPROM.update(i, 0);
  Serial.println(F(" Clear :) "));
  FastLED.clear(); FastLED.show();
}

static void saveSlot(word m) {
  int I = 99 - m;
  EEPROM.update(100 + I, Rr); EEPROM.update(200 + I, Gg); EEPROM.update(300 + I, Bb);
  eePutW(396 + 4 * I, Ns);    eePutW(398 + 4 * I, Nf);
  eePutW(6, Led);
}

static void restoreFromEeprom() {
  Ch0 = EEPROM.read(1); Dan = EEPROM.read(2);
  Rr = EEPROM.read(3);  Gg = EEPROM.read(4); Bb = EEPROM.read(5);
  Led = eeGetW(6);
  byte memoclr = EEPROM.read(9);
  Tim1 = eeGetW(10); Tim2 = eeGetW(12);
  Chekm();
  if (Ch0 > 98 || Ch0 < 1) Ch0 = 1;
  if (Dan > 99) Dan = 0;
  if (Led > 990) Led = 990;
  if (memoclr == 0 || memoclr > 1) { Memclr(); EEPROM.update(9, 1); }
  Danc = Dan;

  if (Dan == 0) {
    if (Led > 0 && Led < 99) {
      for (int I = 1; I <= 98; I++) {
        CRGB c(EEPROM.read(100 + I), EEPROM.read(200 + I), EEPROM.read(300 + I));
        word a = eeGetW(396 + 4 * I), b = eeGetW(398 + 4 * I);
        for (word s = a; s <= b && s < NUM_LEDS; s++) leds[s] = c;
      }
      FastLED.show();
    } else if (Led == 999) {
      fill_solid(leds, NUM_LEDS, CRGB(Rr, Gg, Bb));
      FastLED.show();
    }
  }
}

// ============================================================
void setup() {
  Serial.begin(9600);
  FastLED.addLeds<WS2812B, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(255);
  FastLED.clear(); FastLED.show();
  restoreFromEeprom();
}

// Non-blocking line reader; also called from inside the delays so a new
// command interrupts a running effect immediately (like Bascom Bytematch=13).
static void pollSerial() {
  while (!Ina && Serial.available()) {
    char c = Serial.read();
    if (c == '\r' || c == '\n') {
      if (TextLen > 0) { Text[TextLen] = 0; Ina = true; }
    } else if (TextLen < sizeof(Text) - 1) {
      Text[TextLen++] = c;
    }
  }
}

void loop() {
  pollSerial();
  if (Ina) {
    Red();
    Ina = false; TextLen = 0; Text[0] = 0;
    return;
  }
  if (Danc > 0) Modh();
}

// ============================================================
void Red() {
  // ---- address change:  CH<old>=<new>
  if (Text[0] == 'C' && Text[1] == 'H') {
    char* eq = strchr(Text, '=');
    if (eq && (eq - Text) >= 3 && num(Text + 2, 3) == Ch0) {
      int nw = num(eq + 1, 3);
      if (nw > 0 && nw < 99) {
        Ch0 = nw; EEPROM.update(1, Ch0);
        Serial.print(F("New Address=")); Serial.println(Ch0);
      }
    }
    return;
  }

  byte Ch = 0;
  byte n = strlen(Text); if (n > 32) n = 32;

  for (byte i = 0; i < n; i++) {
    char t = Text[i];
    if (!strchr("ADRGBMSE", t)) continue;
    const char* s = Text + i + 1;

    if (t == 'A') { int v = num(s, 2); if (v > 0 && v < 100) Ch = v; }
    if (Ch != Ch0 && Ch != 99) continue;

    int v = num(s, (t == 'D') ? 2 : 3);
    switch (t) {
      case 'D': if ((v >= 0 && v <= 55) || v == 99) { Danc = v; Dan = v; } break;
      case 'R': if (v < 256) Rr = v; break;
      case 'G': if (v < 256) Gg = v; break;
      case 'B': if (v < 256) Bb = v; break;
      case 'M': Led = v; break;
      case 'S': if (Dan == 0) Ns = v; else Tim1 = v; break;
      case 'E': if (Dan == 0) Nf = v; else Tim2 = v; break;
    }
  }

#if DEBUG
  Serial.print(F("A=")); Serial.print(Ch); Serial.print(F(" D=")); Serial.print(Danc);
  Serial.print(F(" R=")); Serial.print(Rr); Serial.print(F(" G=")); Serial.print(Gg);
  Serial.print(F(" B=")); Serial.print(Bb); Serial.print(F(" S=")); Serial.print(Ns);
  Serial.print(F(" E=")); Serial.print(Nf); Serial.print(F(" M=")); Serial.println(Led);
#endif

  if (Dan < 100) EEPROM.update(2, Dan);

  if (Dan == 0) {
    CRGB c(Rr, Gg, Bb);
    if (Led > 0 && Led < 99) {                 // M1..M98 : paint + save slot
      saveSlot(Led);
      for (word i = Ns; i <= Nf && i < NUM_LEDS; i++) leds[i] = c;
      FastLED.show();
    } else {
      switch (Led) {
        case 0:                                // preview, no save, NO clear
          for (word i = Ns; i <= Nf && i < NUM_LEDS; i++) leds[i] = c;
          FastLED.show();
          break;
        case 996: Memclr(); break;
        case 997: FastLED.clear(); FastLED.show(); delay(100); break;
        case 998: fill_solid(leds, NUM_LEDS, c); FastLED.show(); delay(100); break;
        case 999:
          fill_solid(leds, NUM_LEDS, c); FastLED.show();
          EEPROM.update(3, Rr); EEPROM.update(4, Gg); EEPROM.update(5, Bb);
          eePutW(6, 999);
          break;
      }
    }
  } else {
    if (Led < 1) Led = 1;
    if (Led > 999) Led = 999;
    Chekm();
    if (Danc > 0 && Danc < 100) {
      EEPROM.update(3, Rr); EEPROM.update(4, Gg); EEPROM.update(5, Bb);
      eePutW(6, Led); eePutW(10, Tim1); eePutW(12, Tim2);
    }
  }
}

// ============================================================
void Chekm() {
  if (Tim1 > 999 || Tim1 < 1) Tim1 = 1;
  if (Tim2 > 999 || Tim2 < 1) Tim2 = 1;
}

static void waitMs(word t) {           // t+1 ms, aborts as soon as a full line arrives
  for (word i = 0; i <= t; i++) {
    pollSerial();
    if (Ina) return;
    delay(1);
  }
}
void Time1() { Chekm(); waitMs(Tim1); }
void Time2() { Chekm(); waitMs(Tim2); }

// ============================================================
static byte pickCh(byte v) {
  if (v < 251) return v;
  switch (v) {
    case 251: return rnd(15);
    case 252: return rnd(30);
    case 253: return rnd(60);
    case 254: return rnd(120);
    default:  return rnd(250);
  }
}

void Modh() {
  if (Led == 0) Led = 1;
  if (Led >= NUM_LEDS) Led = NUM_LEDS - 1;      // Led = index of last pixel used

  Dans = (Danc == 99) ? random(55) : Danc - 1;
  Rx = pickCh(Rr); Gx = pickCh(Gg); Bx = pickCh(Bb);

  switch (Dans) {
    case 0:  Snak_run_slow(); break;
    case 1:  Snak_return_slow(); break;
    case 2:  Snak_run_slow(); ABORT; Snak_return_slow(); break;
    case 3:  Snak_run_fast(); break;
    case 4:  Snak_return_fast(); break;
    case 5:  Snak_run_fast(); ABORT; Snak_return_fast(); break;
    case 6:  Snak_run_slow_random(); break;
    case 7:  Snak_return_slow_random(); break;
    case 8:  Snak_run_slow_random(); ABORT; Snak_return_slow_random(); break;
    case 9:  Snak_run_fast_random(); break;
    case 10: Snak_return_fast_random(); break;
    case 11: Snak_run_fast_random(); ABORT; Snak_return_fast_random(); break;
    case 12: Snak_runreturn_slow_random(); break;
    case 13: Nnb = 0; Half_snak_out_mid_slow(); break;
    case 14: Nnb = 1; Half_snak_out_mid_slow(); break;
    case 15: Half_snak_out_mid_slow(); Nnb ^= 1; break;   // no recursion: loop() calls us again
    case 16: Half_half_right_slow(); break;
    case 17: Half_half_left_slow(); break;
    case 18: Mx = 0; Star_run_slow(); break;
    case 19: Mx = 1; Star_run_slow(); break;
    case 20: Star_runreturn_slow(); break;
    case 21: Star_run_fast(); break;
    case 22: Star_return_fast(); break;
    case 23: Star_run_fast(); ABORT; Star_return_fast(); break;
    case 24: Snak_growup_slow(); break;
    case 25: Star_playful(); break;
    case 26: Snak_star_playful(); break;
    case 27: Star_fillers(); break;
    case 28: Movable_light(); break;
    case 29: Snak_runreturn_colored(); break;
    case 30: Star_diamonds_blink_slow(); break;
    case 31: Star_diamonds_blink_fast(); break;
    case 32: Star_diamonds_blink_faster(); break;
    case 33: Disco(); break;
    case 34: Fillstripe_color(); break;
    case 35: Breathing(); break;
    case 36: Percolate_run(); break;
    case 37: Percolate_return(); break;
    case 38: Percolate_run(); ABORT; Percolate_return(); break;
    case 39: Snak_1_2_run(); break;
    case 40: Snak_1_2_return(); break;
    case 41: Snak_1_2_run(); ABORT; Snak_1_2_return(); break;
    case 42: Snak_1_2_run_rev(); break;
    case 43: Snak_1_2_return_rev(); break;
    case 44: Snak_1_2_run_rev(); ABORT; Snak_1_2_return_rev(); break;
    case 45: Snak_relax_run(); break;
    case 46: Snak_relax_return(); break;
    case 47: Snak_relax_run(); ABORT; Snak_relax_return(); break;
    case 48: Rainbo = false; Snak_rainbow(); break;
    case 49: Rainbo = true;  Snak_rainbow(); break;
    case 50: Rainbo = false; Snak_rainbow(); ABORT; Rainbo = true; Snak_rainbow(); break;
    case 51: Rainbo = false; Snak_rainbow_lp(); break;
    case 52: Rainbo = true;  Snak_rainbow_lp(); break;
    case 53: Rainbo = false; Snak_rainbow_lp(); ABORT; Rainbo = true; Snak_rainbow_lp(); break;
    case 54: Snak_rainbow_bl(); break;
    default: delay(1); break;               // D56..D98: Bascom does nothing
  }
}

// ============================================================
// Effects (order of Show / Time follows the Bascom source)
// ============================================================
void Snak_run_slow() {
  CRGB c(Rx, Gx, Bx);
  for (word n = 0; n <= Led; n++) { setPx(n, c); Time1(); FastLED.show(); ABORT; }
  for (word n = 0; n <= Led; n++) { setPx(n, CRGB::Black); Time2(); FastLED.show(); ABORT; }
}

void Snak_return_slow() {
  CRGB c(Rx, Gx, Bx);
  for (int n = Led; n >= 0; n--) { setPx(n, c); Time1(); FastLED.show(); ABORT; }
  for (int n = Led; n >= 0; n--) { setPx(n, CRGB::Black); Time2(); FastLED.show(); ABORT; }
}

void Snak_run_fast() {
  CRGB c(Rx, Gx, Bx);
  for (word n = 0; n <= Led; n += 5) {
    for (byte k = 0; k < 5; k++) setPx(n + k, c);
    Time1(); FastLED.show(); ABORT;
  }
  for (word n = 0; n <= Led; n += 5) {
    for (byte k = 0; k < 5; k++) setPx(n + k, CRGB::Black);
    Time2(); FastLED.show(); ABORT;
  }
}

// Bascom sets Nd..Nd+4 here (upwards), unlike Snak_return_fast_random (downwards)
void Snak_return_fast() {
  CRGB c(Rx, Gx, Bx);
  for (int n = Led; n >= 0; n -= 5) {
    for (byte k = 0; k < 5; k++) setPx(n + k, c);
    Time1(); FastLED.show(); ABORT;
  }
  for (int n = Led; n >= 0; n -= 5) {
    for (byte k = 0; k < 5; k++) setPx(n + k, CRGB::Black);
    Time2(); FastLED.show(); ABORT;
  }
}

void Snak_run_slow_random() {
  CRGB c;
  for (word n = 0; n <= Led; n++) {
    if (n % 2 == 0) c = rndCol();
    setPx(n, c); Time1(); FastLED.show(); ABORT;
  }
  for (word k = 1; k < Led; k++) { shR(); Time2(); FastLED.show(); ABORT; }
  FastLED.clear(); FastLED.show();
}

void Snak_return_slow_random() {
  CRGB c;
  for (int n = Led; n >= 0; n--) {
    if (n % 2 == 0) c = rndCol();
    setPx(n, c); Time1(); FastLED.show(); ABORT;
  }
  for (word k = 1; k < Led; k++) { shL(); Time2(); FastLED.show(); ABORT; }
  FastLED.clear(); FastLED.show();
}

void Snak_run_fast_random() {
  CRGB c;
  for (word n = 0; n <= Led; n += 5) {
    if (n % 2 == 0) c = rndCol();
    for (byte k = 0; k < 5; k++) setPx(n + k, c);
    FastLED.show(); Time1(); ABORT;
  }
  for (word n = 0; n <= Led; n += 5) {
    for (byte k = 0; k < 5; k++) setPx(n + k, CRGB::Black);
    FastLED.show(); Time2(); ABORT;
  }
  FastLED.clear(); FastLED.show();
}

void Snak_return_fast_random() {
  CRGB c;
  for (int n = Led; n >= 4; n -= 5) {
    if (n % 2 == 0) c = rndCol();
    for (byte k = 0; k < 5; k++) setPx(n - k, c);
    Time1(); FastLED.show(); ABORT;
  }
  for (int n = Led; n >= 4; n -= 5) {
    for (byte k = 0; k < 5; k++) setPx(n - k, CRGB::Black);
    Time2(); FastLED.show(); ABORT;
  }
  FastLED.clear(); FastLED.show();
}

void Snak_runreturn_slow_random() {
  CRGB c;
  for (int n = Led; n >= 0; n--) {
    if (n % 2 == 0) c = rndCol();
    setPx(n, c); Time1(); FastLED.show(); ABORT;
  }
  for (word k = 1; k < Led; k++) { shR(); Time2(); FastLED.show(); ABORT; }
  for (word n = 0; n <= Led; n++) {
    if (n % 2 == 0) c = rndCol();
    setPx(n, c); Time1(); FastLED.show(); ABORT;
  }
  for (word k = 1; k < Led; k++) { shL(); Time2(); FastLED.show(); ABORT; }
}

void Half_snak_out_mid_slow() {
  CRGB c(Rx, Gx, Bx);
  word Jk = Led / 2;
  if (Nnb == 0) {
    for (word n = 0; n <= Jk; n++) { setPx(n, c); setPx(Led - n, c); Time1(); FastLED.show(); ABORT; }
  } else {
    for (int n = Jk; n >= 0; n--)  { setPx(n, c); setPx(Led - n, c); Time1(); FastLED.show(); ABORT; }
  }
}

void Half_half_right_slow() {
  FastLED.clear();
  CRGB c(Rx, Gx, Bx);
  word Jk = Led / 2;
  for (word n = 0; n <= Jk; n++) { setPx(n, c); setPx(Jk + n, c); Time1(); FastLED.show(); ABORT; }
}

void Half_half_left_slow() {
  FastLED.clear();
  CRGB c(Rx, Gx, Bx);
  word Jk = Led / 2;
  for (int n = Jk; n >= 0; n--) { setPx(n, c); setPx(Jk + n, c); Time1(); FastLED.show(); ABORT; }
}

void Star_run_slow() {
  if (Chc == 0) { Ado = Led; Chc = 1; }
  if (Ado == 1) Chc = 0;
  CRGB c;
  for (word n = 0; n <= Ado; n += 2) {
    c = (Mx == 0) ? CRGB(Rx, Gx, Bx) : rndCol();
    setPx(n, c); setPx(n + 1, c);
    FastLED.show(); Time1(); FastLED.clear(); ABORT;
  }
  c = (Mx == 0) ? CRGB(222, 222, 222) : CRGB(rnd(222), rnd(222), rnd(222));
  word Dv = Ado / 10;
  if (Ado > 10) { for (word n = Ado; n <= Ado + Dv; n++) setPx(n, c); }
  else          { setPx(Ado, c); }
  FastLED.show(); Time2();
  if (Chc == 1) { if (Ado > 10) Ado -= Dv; else Ado--; }
}

void Star_runreturn_slow() {
  CRGB c(Rx, Gx, Bx);
  for (word n = 0; n <= Led; n += 2) {
    setPx(n, c); setPx(n + 1, c); setPx(n + 2, c);
    FastLED.show(); Time1(); FastLED.clear(); ABORT;
  }
  for (int n = Led; n >= 0; n -= 2) {
    setPx(n, c); setPx(n + 1, c); setPx(n + 2, c);
    FastLED.show(); Time2(); FastLED.clear(); ABORT;
  }
}

void Star_run_fast() {
  CRGB c(Rx, Gx, Bx);
  for (word n = 0; n <= Led; n += 5) {
    for (byte k = 0; k < 5; k++) setPx(n + k, c);
    FastLED.show(); Time1(); FastLED.clear(); ABORT;
  }
}

void Star_return_fast() {
  CRGB c(Rx, Gx, Bx);
  for (int n = Led; n >= 5; n -= 5) {
    for (byte k = 0; k < 5; k++) setPx(n - k, c);
    FastLED.show(); Time2(); FastLED.clear(); ABORT;
  }
}

void Snak_growup_slow() {
  CRGB c(Rx, Gx, Bx);
  for (byte n = 1; n <= 4; n++) setPx(n, c);
  FastLED.show();
  if (T < Led) T += 5;
  if (T >= Led) T = 1;
  for (word k = 1; k <= Led - T; k++) { shR(); Time1(); FastLED.show(); ABORT; }
  for (int n = Led - 1; n >= (int)Led - 3 && n >= 0; n--) { setPx(n, c); FastLED.show(); }
  for (word k = 1; k <= Led - T; k++) { shL(); Time2(); FastLED.show(); ABORT; }
}

void Star_playful() {
  CRGB c(Rx, Gx, Bx);
  for (byte n = 1; n <= 4; n++) setPx(n, c);
  FastLED.show();
  word Yfd = rnd(5);
  if (T < Led) T += Yfd;
  if (T >= Led) T = 1;
  for (word k = 1; k <= Led - T; k++) { shR(); Time1(); FastLED.show(); ABORT; }
  word Xfd = rnd(5);
  for (int n = Led - 1; n >= (int)Led - (int)Xfd; n -= 5) { setPx(n, c); FastLED.show(); }
  word Vfd = rnd(T);
  for (word k = 1; k <= Led - Vfd; k++) { shL(); Time2(); FastLED.show(); ABORT; }
}

// NOTE: forces Led>=22 like Bascom; on a 14-pixel strip part of the motion is off-strip.
void Snak_star_playful() {
  if (Led < 22) Led = 22;
  CRGB c(Rx, Gx, Bx);
  int  L = Led;
  byte Pp = rnd(10); if (!Pp) Pp = 1;
  for (byte n = 1; n <= Pp; n++) setPx(n, c);
  FastLED.show();

  Pp = rnd(10); if (!Pp) Pp = 1;
  if (T < Led) T++;
  if (T >= Led) T = 1;
  T = rnd(T); T += Pp; if (T == 0) T = 1;

  for (int n = 1; n <= L - (int)T; n += Pp) { shR(); FastLED.show(); Time1(); ABORT; }
  for (int n = L - 1; n >= L - (int)T; n -= 3) { if (n > 0) { setPx(n, c); FastLED.show(); } }

  Pp = rnd(10); if (!Pp) Pp = 3;
  for (int n = 1; n <= L - (int)T; n += Pp) { shL(); FastLED.show(); Time2(); ABORT; }

  Pp = rnd(10); if (!Pp) Pp = 1;
  if (T < Led) T++;
  if (T >= Led) T = 1;
  T = rnd(T); T += Pp; if (T == 0) T = 1;
  int Md = rnd(Led); if (Md < (int)T) Md = T + 1;
  for (int n = 1; n <= Md - (int)T; n += Pp) {
    int Jd = L - Pp;
    if (n > 0) setPx(n, c);
    if (Jd > 0) setPx(Jd, c);
    FastLED.show(); Time1(); ABORT;
  }
  // Bascom: "For Nd = Led-t To Led-1 Step -3" (start < end with negative step) - ported as ascending
  for (int n = L - (int)T; n <= L - 1; n += 3) { if (n > 0) setPx(n, c); }
  FastLED.show();

  Pp = rnd(10); if (!Pp) Pp = 3;
  for (int n = L - (int)T; n >= 1; n -= Pp) { shR(); FastLED.show(); Time2(); ABORT; }
  for (byte n = 1; n <= Pp; n++) setPx(n, c);
  FastLED.show();
}

void Star_fillers() {
  if (Ado < Led) Ado++;
  if (Ado == Led) Ado = 1;
  CRGB c(Rx, Gx, Bx);
  for (word n = 1; n <= Ado; n += 4) { setPx(n, c); FastLED.show(); }
  // Bascom: "For N = 1 To Led-ado Step -4" - ported as ascending (see notes)
  for (int k = 1; k <= (int)Led - (int)Ado; k += 4) { shR(); Time1(); FastLED.show(); ABORT; }
  for (int n = Led - 1; n >= (int)Led - (int)Ado; n -= 5) { setPx(n, c); FastLED.show(); }
  for (word k = 1; k <= Led - Ado; k++) { shL(); Time2(); FastLED.show(); ABORT; }
}

void Movable_light() {
  CRGB white(222, 222, 222);
  CRGB col(rnd(Rx), rnd(Gx), rnd(Bx));
  for (word n = 0; n <= Led; n++) {
    setPx(n + 3, white); setPx(n + 2, white); setPx(n + 1, white);
    setPx(n, col);
    Time1(); FastLED.show(); ABORT;
  }
  col = CRGB(rnd(Rx), rnd(Gx), rnd(Bx));
  for (int n = Led; n >= 0; n--) {
    setPx(n, white); setPx(n + 1, white); setPx(n + 2, white);
    setPx(n + 3, col);
    Time2(); FastLED.show(); ABORT;
  }
}

void Snak_runreturn_colored() {
  CRGB c = rndCol();
  for (word n = 0; n <= Led; n++) { setPx(n, c); Time1(); FastLED.show(); ABORT; }
  c = rndCol();
  for (int n = Led; n >= 0; n--) { setPx(n, c); Time2(); FastLED.show(); ABORT; }
}

void Star_diamonds_blink_slow() {
  CRGB c = rndCol();
  setPx(rnd(Led), c);
  Time1(); ABORT;
  setPx(rnd(Led), c);
  Time2();
  FastLED.show();
}

void Star_diamonds_blink_fast() {
  CRGB c(Rx, Gx, Bx);
  Time1(); Time2(); ABORT;
  setPx(rnd(Led), c);
  FastLED.show(); FastLED.clear();
}

void Star_diamonds_blink_faster() {
  CRGB c(Rx, Gx, Bx);
  Time1(); Time2(); ABORT;
  for (byte k = 0; k < 8; k++) setPx(rnd(Led), c);
  FastLED.show(); FastLED.clear();
}

void Disco() {
  CRGB c(Rx, Gx, Bx);
  for (word i = 0; i <= Led; i++) { Time1(); ABORT; setPx(rnd(Led), c); }
  FastLED.show();
  for (word i = 0; i <= Led; i++) { Time2(); ABORT; setPx(rnd(Led), CRGB::Black); }
  FastLED.show();
}

void Fillstripe_color() {                    // Rb_fillstripe = whole strip
  fill_solid(leds, NUM_LEDS, rndCol());
  FastLED.show();
  for (word i = 0; i <= 200; i++) { Time1(); ABORT; Time2(); ABORT; }
}

void Breathing() {
  byte A1 = Rx, A2 = Gx, A3 = Bx;
  for (word l = 0; l <= 255; l++) {
    if (A1 == 1 || A2 == 1 || A3 == 1) break;
    if (A1 > 1) A1--; if (A2 > 1) A2--; if (A3 > 1) A3--;
    fill_solid(leds, NUM_LEDS, CRGB(A1, A2, A3));
    FastLED.show(); Time1(); ABORT;
  }
  for (word l = 0; l <= 255; l++) {
    if (A1 == Rx || A2 == Gx || A3 == Bx) break;
    if (A1 < Rx) A1++; if (A2 < Gx) A2++; if (A3 < Bx) A3++;
    fill_solid(leds, NUM_LEDS, CRGB(A1, A2, A3));
    FastLED.show(); Time2(); ABORT;
  }
}

void Percolate_run() {
  CRGB c(Rx, Gx, Bx);
  word Jk = Led / 2;
  for (word n = 0; n <= Jk; n++) {
    setPx(n, c); setPx(Led - n, c);
    Time1(); FastLED.show(); FastLED.clear(); ABORT;
  }
}

void Percolate_return() {
  CRGB c(Rx, Gx, Bx);
  word Jk = Led / 2;
  for (int n = Jk; n >= 0; n--) {
    setPx(n, c); setPx(Led - n, c);
    delay(1); pollSerial(); FastLED.show(); FastLED.clear(); ABORT;
  }
}

static void clampMin10(CRGB &c) { if (c.r < 10) c.r = 10; if (c.g < 10) c.g = 10; if (c.b < 10) c.b = 10; }

void Snak_1_2_run() {
  FastLED.clear();
  CRGB c(Rx, Gx, Bx);
  for (word n = 0; n <= Led; n += 2) { setPx(n, c); FastLED.show(); Time1(); ABORT; }
  c = rndCol(); clampMin10(c);
  for (word n = 1; n <= Led; n += 2) { setPx(n, c); FastLED.show(); Time2(); ABORT; }
}

void Snak_1_2_return() {
  word Dv = Led + 1;
  FastLED.clear();
  CRGB c(Rx, Gx, Bx);
  for (int n = Led; n >= 0; n -= 2) { setPx(n, c); FastLED.show(); Time1(); ABORT; }
  c = rndCol(); clampMin10(c);
  for (int n = Dv; n >= 0; n -= 2) { setPx(n, c); FastLED.show(); Time2(); ABORT; }
}

void Snak_1_2_run_rev() {
  FastLED.clear();
  CRGB c(Rx, Gx, Bx);
  for (word n = 0; n <= Led; n += 2) { setPx(n, c); FastLED.show(); Time1(); ABORT; }
  c = rndCol(); clampMin10(c);
  for (int n = Led; n >= 0; n -= 2) { setPx(n, c); FastLED.show(); Time2(); ABORT; }
}

void Snak_1_2_return_rev() {
  FastLED.clear();
  CRGB c(Rx, Gx, Bx);
  for (int n = Led; n >= 0; n -= 2) { setPx(n, c); FastLED.show(); Time1(); ABORT; }
  c = rndCol(); clampMin10(c);
  for (word n = 0; n <= Led; n += 2) { setPx(n, c); FastLED.show(); Time2(); ABORT; }
}

void Snak_relax_run() {
  FastLED.clear();
  byte Xf = (Rx > 10) ? Rx / 10 : 0, Yf = (Gx > 10) ? Gx / 10 : 0, Vf = (Bx > 10) ? Bx / 10 : 0;
  byte r = 0, g = 0, b = 0;
  for (int n = Led; n >= (int)Led - 10; n--) { r += Xf; g += Yf; b += Vf; setPx(n, CRGB(r, g, b)); }
  for (int n = (int)Led - 11; n >= (int)Led - 20; n--) { r -= Xf; g -= Yf; b -= Vf; setPx(n, CRGB(r, g, b)); }
  FastLED.show();
  for (word k = 0; k <= Led; k++) { shL(); Time1(); FastLED.show(); ABORT; }
}

void Snak_relax_return() {
  FastLED.clear();
  byte Xf = (Rx > 10) ? Rx / 10 : 0, Yf = (Gx > 10) ? Gx / 10 : 0, Vf = (Bx > 10) ? Bx / 10 : 0;
  byte r = 0, g = 0, b = 0;
  for (word n = 0; n <= 10; n++)  { r += Xf; g += Yf; b += Vf; setPx(n, CRGB(r, g, b)); }
  for (word n = 11; n <= 21; n++) { r -= Xf; g -= Yf; b -= Vf; setPx(n, CRGB(r, g, b)); }
  FastLED.show();
  for (word k = 0; k <= Led; k++) { shR(); Time1(); FastLED.show(); ABORT; }
}

static void rainbowRotate() {                 // Bascom: Rainbo=0 -> Rb_rotateright, 1 -> Rb_rotateleft
  for (word k = 0; k <= Led; k++) {
    if (!Rainbo) rotR(); else rotL();
    Time1(); FastLED.show(); ABORT;
  }
}

void Snak_rainbow() {
  for (byte I = 0; I <= 23; I++) {
    word Bf = 20 * I;
    if (Bf >= NUM_LEDS) break;
    CRGB c = rndCol();
    byte Xf = (c.r > 10) ? c.r / 10 : 0, Yf = (c.g > 10) ? c.g / 10 : 0, Vf = (c.b > 10) ? c.b / 10 : 0;
    byte r = 0, g = 0, b = 0;
    word Jk = 10 + Bf, Lk = 20 + Bf;
    for (word n = Bf; n <= Jk; n++) { r += Xf; g += Yf; b += Vf; setPx(n, CRGB(r, g, b)); }
    for (word n = Jk; n <= Lk; n++) { r -= Xf; g -= Yf; b -= Vf; setPx(n, CRGB(r, g, b)); }
  }
  FastLED.show();
  rainbowRotate();
}

void Snak_rainbow_lp() {
  for (byte I = 0; I <= 23; I++) {
    word Bf = 20 * I;
    if (Bf >= NUM_LEDS) break;
    for (word n = Bf; n <= 20 + Bf; n++) setPx(n, rndCol());
  }
  FastLED.show();
  rainbowRotate();
}

void Snak_rainbow_bl() {
  FastLED.clear();
  for (byte I = 0; I <= 23; I++) {
    word Bf = 20 * I;
    if (Bf >= NUM_LEDS) break;
    for (word n = Bf; n <= 20 + Bf; n++) setPx(n, rndCol());
  }
  FastLED.show();
  Time1(); ABORT;
  rotL();
  FastLED.show();
  FastLED.clear();
  Time2();
}

Credits

Saeid Moghadam
6 projects • 4 followers
Electrical and electronics engineer sharing simple beginner-friendly projects. Always learning, improving, and welcoming feedback.

Comments