RANGMANG

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

RANGMANG RGB Controller Module

LED Strip Types and Features

The following table introduces various LED strips and their specifications:

LED Strip Model Operating Voltage Control Type Addressable Special Features
WS281112VDigitalYesSuitable for long strips
WS2812B5VDigitalYesBuilt-in chip in each LED
WS28135VDigitalYesResistant to data line failure
WS281512VDigitalYesIndustrial-grade, low voltage drop
SK68125VDigitalYesSupports RGBW (white channel)
SK98225VDigitalYesHigh-speed, similar to APA102
APA1025VDigital (Clock + Data)YesFast animations, dual-wire control
TM181412VDigitalYesRGBW, high-power applications

Official download link for USBasp Programmer: You can find complete files and information about the USBasp programmer on the developer's official website: fischl.de/usbasp.

PCB and Schematic Files Atmega 128A

ATmega128A PCB Schematic

PCB and Schematic Files Atmega 328P

ATmega328P PCB Schematic

RANGMANG Dance Mode Demo

RANGMANG Dance Mode Configuration

This configuration defines how the RANGMANG module controls LED dance modes. Each row encodes the following parameters (ADRGBSEM format):

A (Channel) D (Mode) R (Red) G (Green) B (Blue) S (Dance Time 1) E (Dance Time 2) M (Pixel Count)
A1D12R251G251B251 S1E1M128

RANGMANG Fixed Color Mode Configuration (ATmega128 – 999 Pixels)

A (Channel) D (Mode) R (Red) G (Green) B (Blue) S (Start Pixel) E (End Pixel) M (Memory Slot)
A1D0R200G50B100 S1E120M10

Power Regulation Notes

If the module is used individually, connect the Bluetooth power jumper.

If the modules are used in parallel, only connect the jumper for the module linked to Bluetooth and disconnect the rest.

Install a 5V regulator for 12V LED strips; for 5V LED strips, no regulator is needed.

Use a single wire to solder and connect the input and output pins of the regulator directly on the board.

RANGMANG ATmega128 – 999 Pixels

' Inputs format: ADRGBSEM (Leading zeros are optional)
' Example: A1D0R0G0B0S0E0M0
' A1 TO A98    FOR ADDRESS MODULE   A99 FOR ALL
' IF D0 THEN SOLID COLOR
' R: 0 TO 255   RED COLOR
' G: 0 TO 255   GREEN COLOR
' B: 0 TO 255   BLUE COLOR
' S: 0 TO 999   RGB STRIP START POSITION FOR COLORED PIXEL
' E: 0 TO 999   RGB STRIP END POSITION FOR COLORED PIXEL
' M: 0 TO 98    M0 WITHOUT MEMORY SAVE   M1 TO M98 SAVE SLOT MEMORY

' IF D1 TO D55   DANCE
' D99   FOR RANDOM SELECT D1 TO D55 DANCE
' R: 0 TO 250   RED COLOR   251 TO 255 FOR RANDOM RED COLOR
' G: 0 TO 250   GREEN COLOR   251 TO 255 FOR RANDOM GREEN COLOR
' B: 0 TO 250   BLUE COLOR   251 TO 255 FOR RANDOM BLUE COLOR
' S: 0 TO 999   RGB STRIP FIRST DELAY EFFECT (ms)
' E: 0 TO 999   RGB STRIP SECOND DELAY EFFECT (ms)
' M: 1 TO 999   SET HOW MANY PIXELS DANCING

RANGMANG ATmega328 – 450 Pixels

' Inputs format: ADRGBSEM (Leading zeros are optional)
' Example: A1D0R0G0B0S0E0M0
' A1 TO A98    FOR ADDRESS MODULE   A99 FOR ALL
' IF D0 THEN SOLID COLOR
' R: 0 TO 255   RED COLOR
' G: 0 TO 255   GREEN COLOR
' B: 0 TO 255   BLUE COLOR
' S: 0 TO 450   RGB STRIP START POSITION FOR COLORED PIXEL
' E: 0 TO 450   RGB STRIP END POSITION FOR COLORED PIXEL
' M: 0 TO 98    M0 WITHOUT MEMORY SAVE   M1 TO M98 SAVE SLOT MEMORY
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'M996 performs a full pixel wipe on the LED strip and erases all stored memory.
'M997 clears all pixels on the LED strip without affecting any memory
'M998 quickly fills all pixels on the LED strip without using or modifying memory
'M999 colors all pixels on the LED strip and saves it as memory slot 1

' IF D1 TO D55   DANCE
' D99   FOR RANDOM SELECT D1 TO D55 DANCE
' R: 0 TO 250   RED COLOR   251 TO 255 FOR RANDOM RED COLOR
' G: 0 TO 250   GREEN COLOR   251 TO 255 FOR RANDOM GREEN COLOR
' B: 0 TO 250   BLUE COLOR   251 TO 255 FOR RANDOM BLUE COLOR
' S: 0 TO 999   RGB STRIP FIRST DELAY EFFECT (ms)
' E: 0 TO 999   RGB STRIP SECOND DELAY EFFECT (ms)
' M: 1 TO 450   SET HOW MANY PIXELS DANCING

RANGMANG Arduino Version (ATmega328P)

This is an Arduino equivalent of the ATmega328 Bascom firmware above. It uses the same serial protocol (9600 baud, ADRGBSEM commands, dance modes D1–D55 and D99, memory slots M1–M98, M996–M999, and address change with CH1=2), so the Soifgo app and your existing commands work the same way.

How to use: install the FastLED library in the Arduino IDE, connect the LED data line to pin D5, set NUM_LEDS to your pixel count, and upload. For strips with swapped green/blue (some WS2811), change COLOR_ORDER to RBG.

Wiring

LED strip (WS2812B)      Arduino / power
  DATA  ----[330 ohm]--->  D5  (digital pin 5)
  5V    ----------------->  external 5V supply (+)
  GND   ----------------->  external supply (-)  AND Arduino GND

Bluetooth module (HC-05 / HC-06)
  VCC   ----------------->  5V
  GND   ----------------->  GND
  TX    ----------------->  D0 (RX)
  RX    <---[1k]---+---[2k]--- GND      (voltage divider)
                   +------------------  D1 (TX)

Please note: this version may not behave exactly like the Bascom version. Some effects can look slightly different. The code is open source, so if you find a difference or a problem, you are free to paste it into an AI assistant and ask it to fix or optimize it for you.

Make it yours: if you do not like a dance effect, simply delete its case line in the Modh() function, or replace it with another effect. You can also change, shorten or rewrite any effect function to suit your own project.

/*
 * 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();
}

✅ Module Addressing Instructions

To address a module, simply follow the method below.

For example:
Old address 1   New Address 2  →  CH1=2
Old address 23  New Address 47 →  CH23=47

Important:
After programming, all modules have an initial default address of 1.
Therefore, during the first addressing, the old (current) address is always 1.

Addressing format:
CH old_address = new_address

For example:
CH1=3

After changing the address, the module will respond using its new address.
For example, if the new address is 3, the module will reply to:
New Address=3

Please note:
Valid addresses range from 1 to 98.
Address 99 is a shared address that applies to all modules.

Therefore, each module will respond and act to its assigned address (between 1 and 98) as well as to the shared address 99.

In each microcontroller's folder, the software directory "dude 6.4" is available. Using the file "Program Chip.bat," you simply need to connect the USBasp to the microcontroller and then click on "Program Chip.bat" to program the microcontroller.

Soifgo Design for RANGMANG RANGMANG Design 1 RANGMANG Design 2

Color Swap Issue: WS2811 vs WS2812

When working with addressable LED strips like the WS2811 and WS2812, you might notice a classic issue: when you test your LEDs, the Red color displays perfectly, but Green and Blue are completely swapped.

Why does this happen?

While the Red channel remains in its correct first position, the order of the Green and Blue channels varies due to hardware layout designs:

As a result, from a programmer's and user's perspective, the strip ends up behaving as an RBG (Red, Blue, Green) device instead of a standard RGB device.

Why We Inverted the Logic in the Code

To prevent any confusion or "color dizziness" while designing your light patterns, Soifgo handles all the heavy lifting behind the scenes.

Most modern WS2811 strips available on the market today use an internal RBG chip methodology. However, to keep things intuitive for you, the app lets you think and work in the universal RGB standard.

When you pick a color in the app—for example, setting R=123, G=22, B=12—you don't have to worry about the strip's internal layout. When the RBG mode is active:

In short, we deliberately inverted the software logic for the WS2811 setting so that your workflow remains simple, consistent, and strictly RGB on the screen, while the hardware gets exactly what it needs!

💡 Solution in Soifgo Settings

If you are experiencing this color mismatch, there is no need to change your hardware wiring. You can fix this instantly via software inside the app:

Important Note for Soifgo Users: When using certain WS2811 LED strips where the Green and Blue colors are reversed, simply go to the application settings and tap on the RGB label to switch it to RBG.

By changing this setting to RBG, Soifgo will automatically swap the Green and Blue bytes during data transmission, ensuring that your color palette and light animations are applied accurately to your strip!

💻 Developer Note: Coding Static Colors for WS2811

If you are writing your own custom firmware or setting up static colors directly in your microcontroller code for a WS2811 strip, you must manually swap the Green and Blue values to achieve the correct output.

Since the physical hardware layout of these strips interprets data differently than our standard mental model, you need to trick the strip by reversing the variables.

For Example:
If your desired color is Red: 10, Green: 123, Blue: 44, writing it in the standard order will result in swapped colors on the strip.

To fix this in your code, you must pass the values like this:

Standard RGB Intended Color: R: 10, G: 123, B: 44

Actual Code to Write for WS2811: R: 10, G: 44, B: 123

By intentionally swapping the Green (123) and Blue (44) values inside your code, the WS2811 strip will display the exact shade you originally intended!

The default baud rate of the Bluetooth module is 9600

Downloads

Hardware Assembly Notes

If you need the module as a single unit, you can use regular sockets. However, when using multiple modules in parallel, only the Bluetooth socket should be connected through long-pin headers so that the remaining modules can receive Bluetooth serial data.

Next, it is better to mechanically connect the modules together using spacer screws. For the final module, you can insulate the pins or use a suitable cover. Alternatively, if you are certain that you will not expand the number of modules in the future, you may use short-pin headers instead.

Board Assembly Diagram

⚠️ Important Power Note

When using long RGB strips, make sure to pay attention to the voltage of the microcontroller and Bluetooth. Using an unsuitable regulator or a power supply with insufficient current capacity can cause severe voltage drops, which may lead to the Bluetooth and microcontroller shutting down.


⬅ Back to Home