WS2812 a MPU6050 jako "světelný meč"

Tvoříte zajímavý projekt? Pochlubte se s ním.
Pravidla fóra
Vkládejte prosím jen HOTOVÉ projekty, které chcete představit ostatním.
Odpovědět
jankop
Příspěvky: 1029
Registrován: 06 zář 2017, 20:04
Reputation: 0
Bydliště: Brno
Kontaktovat uživatele:

WS2812 a MPU6050 jako "světelný meč"

Příspěvek od jankop » 06 pro 2022, 18:51

Pro inspiraci.
Udělal jsem experimentální prototyp světelného meče s WS2812 a Arduinem. Obrázek je s Arduinem Mini Pro, ale daleko pohodlnější na ladění je Arduino Nano, které jsem použil. Zapojení je velmi jednoduché. Tlačítko na pinu 3 je na volbu efektů. Gyroskop/akcelerometr MPU6050 je laciná krásná hračka a zajišťuje rozsvícení "meče" při pohybu.
Sword_Schema.png

Kód: Vybrat vše

// Version 1.08
// Tested with Arduino IDE v.1.8.19 and Arduino core v.1.8.6
// 11/2022 Pavel Janko, www.fancon.cz
// Nezbytne knihovny
#include <Adafruit_NeoPixel.h>  // version 1.10.6    
#include <Adafruit_MPU6050.h>   // version 2.2.4  
#include <Adafruit_Sensor.h>    // Adafruit Unified Sensor version 1.1.6
#include <Wire.h>
const uint8_t WS2812_PIN = 17;      // Vystupni pin Arduina pro data LED pasku WS2812
const uint8_t ButtonPin = 3;        // Vstupni pin Arduina pro tlacitko
const uint8_t StringPixelsNum = 29; // Pocet pouzitých LED v pasku WS2812
const int TasPeriod = 8;            // Rychlost rozsviceni jednotlivych LED mece v milisekundach
const int UntasPeriodConst = 15;    // Rychlost zhaseni jednotlivych LED mece v milisekundach
const int ShotPeriod = 20;          // Rychlost beziciho kratkeho paprsku
int UntasPeriod = UntasPeriodConst;
//********************************************************************************
unsigned long TasTime;
unsigned long UntasTime;
unsigned long ShotTime;
bool tas = false;
bool untas = false;
int16_t Counter01 = 0;
uint8_t Func = 2;
uint8_t Rcolor = 128;
uint8_t Gcolor = 0;
uint8_t Bcolor = 0;
// data pro vytvareni duhy
const byte ForFill[] PROGMEM = {128, 0, 0, 120, 0, 0, 110, 10, 0, 100, 20, 0, 90, 30, 0, 80, 40, 0, 70, 50, 0,
                                60, 60, 0, 50, 70, 0, 40, 80, 0, 30, 90, 0, 20, 100, 0, 10, 110, 0, 0, 120, 0,
                                0, 128, 0, 0, 120, 0, 0, 110, 10, 0, 100, 20, 0, 90, 30, 0, 80, 40, 0, 70, 50, 0, 60, 60,
                                0, 50, 70, 0, 40, 80, 0, 30, 90, 00, 20, 100, 0, 10, 110, 0, 0, 120, 0, 0, 128
                               };

// inicializace procesu gyroskopu a LED pasku
Adafruit_MPU6050 mpu;
Adafruit_NeoPixel pixels(StringPixelsNum, WS2812_PIN);
void setup() {
  //Serial.begin(115200);
  //Nastav I2C na 400 000Hz
  Wire.setClock(400000);
  // start procesu gyroskopu a LED pasku
  mpu.begin();
  pixels.begin();
  //Nastavení V/V pinu
  pinMode(ButtonPin, INPUT_PULLUP);
  pinMode(WS2812_PIN, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  // zablikani LED na Arduinu na zacatku programu
  for (int i = 0; i < 10; i++) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    delay(50);
  }
  //****************************************************
  // Blokace svitu LED pri kompilaci
  // sviti jen slabe cervena
  //  pixels.clear();
  //  pixels.setPixelColor(0, pixels.Color(8, 0, 0));
  //  pixels.show();
  //  while (digitalRead(ButtonPin)) {}
  //****************************************************
  // rozsvícení prvni cervene LED v pasku
  pixels.clear();
  pixels.setPixelColor(0, pixels.Color(Rcolor, Gcolor, Bcolor));
  pixels.show();
  // Nastavení rozsahu MPU6050, viz zalozka MPU6050 deklarace
  mpu.setAccelerometerRange(MPU6050_RANGE_2_G);
  mpu.setGyroRange(MPU6050_RANGE_250_DEG);
  mpu.setFilterBandwidth(MPU6050_BAND_5_HZ);
}
void loop() {
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  // Kontrola tlacitka a prepinani barev a funkci
  if (!digitalRead(ButtonPin)) {
    delay(50);
    if (!digitalRead(ButtonPin)) {

      Counter01 = 0;
      // zakaz rozsviceni mece
      tas = false;
      // nastaveni barvy mece
      Func++;
      if (Func > 9)Func = 1;
      if (Func == 1) {
        Rcolor = 128;
        Gcolor = 0;
        Bcolor = 0;
      }
      if (Func == 2) {
        Rcolor = 0;
        Gcolor = 128;
        Bcolor = 0;
      }
      if (Func == 3) {
        Rcolor = 0;
        Gcolor = 0;
        Bcolor = 128;
      }
      if (Func == 4) {
        Rcolor = 64;
        Gcolor = 64;
        Bcolor = 0;
      }
      if (Func == 5) {
        Rcolor = 64;
        Gcolor = 0;
        Bcolor = 64;
      }
      if (Func == 6) {
        Rcolor = 0;
        Gcolor = 64;
        Bcolor = 64;
      }
      // duha
      if (Func == 7) {
        Rcolor = 32;
        Gcolor = 32;
        Bcolor = 32;
      }
      if (Func == 8) {
        Rcolor = 64;
        Gcolor = 64;
        Bcolor = 0;
      }
      if (Func == 9) {
        Rcolor = 8;
        Gcolor = 8;
        Bcolor = 8;
      }

      // rozsvit jednu LED dane barvy pro informaci o zvolene funkci
      while (!digitalRead(ButtonPin)) {}
      pixels.clear();
      pixels.setPixelColor(0, pixels.Color(Rcolor, Gcolor, Bcolor));
      pixels.show();
    }
  }
  if (Func > 0 && Func < 7) {
    // Cteni dvou os gyroskopu pro aktivaci rozsvícení LED
    if ((abs(g.gyro.x) > 1.0 || abs(g.gyro.z) > 1.0) && !tas) {
      tas = true;       // povol rozsviceni LED
      untas = false;    // blokuj zhaseni LED
    }
    // Rozsvit mec
    if (((millis() - TasPeriod) > TasTime) && tas) {
      TasTime = millis();
      if (Counter01 < (StringPixelsNum)) {
        pixels.setPixelColor(Counter01, pixels.Color(Rcolor, Gcolor, Bcolor));
        pixels.show();
        Counter01++;
      }
      // když jsou vsechny LED rozsviceny
      if (Counter01 >= StringPixelsNum) {
        tas = false; // blokuj rozsviceni LED
        untas = true;// povol zhaseni LED
        UntasPeriod = 250; // nastav prodlevu pred zhasenim LED
        UntasTime = millis();
      }
    }
    // zhasni mec
    if (((millis() - UntasPeriod) > UntasTime) && untas) {
      UntasPeriod = UntasPeriodConst;
      UntasTime = millis();
      Counter01--;
      if (Counter01 >= 0) {
        pixels.setPixelColor(Counter01, pixels.Color(0, 0, 0));
        pixels.show();
      }
      // když je vše zhasnuto, zakaz zhaseni
      if (Counter01 <= 0) {
        untas = false; // blokuj zhaseni LED
        Counter01 = 0;
      }
    }
  }

  if (Func == 7) {
    // Cteni dvou os gyroskopu pro aktivaci rozsvícení LED
    if ((abs(g.gyro.x) > 1.0 || abs(g.gyro.z) > 1.0) && !tas) {
      tas = true;       // povol rozsviceni LED
      untas = false;    // blokuj zhaseni LED
    }
    // Rozsvit duhu
    if (((millis() - TasPeriod) > TasTime) && tas) {
      TasTime = millis();
      if (Counter01 < (StringPixelsNum)) {
        pixels.setPixelColor(Counter01, pixels.Color(pgm_read_byte_near(ForFill + Counter01 * 3),
                             pgm_read_byte_near(ForFill + Counter01 * 3 + 1),
                             pgm_read_byte_near(ForFill + Counter01 * 3 + 2)));
        pixels.show();
        Counter01++;
      }
      // když jsou vsechny LED rozsviceny
      if (Counter01 >= StringPixelsNum) {
        tas = false; // blokuj rozsviceni LED
        untas = true;// povol zhaseni LED
        UntasPeriod = 250; // nastav prodlevu pred zhasenim LED
        UntasTime = millis();
      }
    }
    // zhasni duhu
    if (((millis() - UntasPeriod) > UntasTime) && untas) {
      UntasPeriod = UntasPeriodConst;
      UntasTime = millis();
      Counter01--;
      if (Counter01 >= 0) {
        pixels.setPixelColor(Counter01, pixels.Color(0, 0, 0));
        pixels.show();
      }
      // když je vše zhasnuto, zakaz zhaseni
      if (Counter01 <= 0) {
        untas = false; // blokuj zhaseni LED
        Counter01 = 0;
      }
    }
  }
  // bezici paprsek
  if (Func == 8) {
    if (((millis() - ShotPeriod) > ShotTime)) {
      ShotTime = millis();
      if (Counter01 < (StringPixelsNum)) {
        pixels.clear();
        pixels.setPixelColor(Counter01, pixels.Color(Rcolor, Gcolor, Bcolor));
        pixels.setPixelColor(Counter01 + 1, pixels.Color(Rcolor, Gcolor, Bcolor));
        pixels.setPixelColor(Counter01 + 2, pixels.Color(Rcolor, Gcolor, Bcolor));
        pixels.setPixelColor(Counter01 + 3, pixels.Color(Rcolor, Gcolor, Bcolor));
        pixels.setPixelColor(Counter01 + 4, pixels.Color(Rcolor, Gcolor, Bcolor));
        pixels.setPixelColor(Counter01 + 5, pixels.Color(0, 0, 0));
        pixels.setPixelColor(Counter01 + 6, pixels.Color(0, 0, 0));
        pixels.setPixelColor(Counter01 + 7, pixels.Color(0, 0, 0));
        pixels.setPixelColor(Counter01 + 8, pixels.Color(0, 0, 0));
        pixels.setPixelColor(Counter01 + 9, pixels.Color(0, 0, 0));
        pixels.show();
        Counter01++;
      }
      else Counter01 = 0;
    }
  }
  // bezici duha
  if (Func == 9) {
    if (((millis() - ShotPeriod) > ShotTime)) {
      ShotTime = millis();
      Counter01++;
      if (Counter01 >= StringPixelsNum )Counter01 = 0;
      for (uint8_t i = 0; i < StringPixelsNum; i++) {
        pixels.setPixelColor(i, pixels.Color(pgm_read_byte_near(ForFill + Counter01 * 3),
                                             pgm_read_byte_near(ForFill + Counter01 * 3 + 1),
                                             pgm_read_byte_near(ForFill + Counter01 * 3 + 2)));
        Counter01++;
        if (Counter01 >= StringPixelsNum )Counter01 = 0;
      }
      pixels.show();

    }

  }
}
V programu se můžete inspirovat, jak časovat bez příkazu delay() i jak využít PROGMEM pro data. S tím gyroskopem můžete dělat všelijaká kouzla a WS2812 je také bezvadná hračka. Vaší fantazii se meze nekladou. Program lze donekonečna zlepšovat, zůstává překvapivě hodně paměti a to jsem se nijak neomezoval. Určitě by bylo možné použít volání funkcí, ale měl jsem trochu obavy (asi zbytečné) z časování WS2812. Můj prototyp napájím z PowerBanky 5V/1A a mám připojeno 29 LED. Protože odběr LED při větším počtu není malý, tak redukuji příkon přímo v programu. Svítivost je ovšem skvělá a docela dlouho to vydrží.
Přikládám také orientační tabulku spotřeby.
ws2812.png

KamilV
Příspěvky: 479
Registrován: 03 dub 2018, 15:27
Reputation: 0
Bydliště: Olomouc

Re: WS2812 a MPU6050 jako "světelný meč"

Příspěvek od KamilV » 06 pro 2022, 19:19

Pěkné ;) Drobnou optimalizací by mohly být konstrukce

Kód: Vybrat vše

if(...) {
    ...
} else if(...) {
    ...
}
v místech, kde z principu více ifů za sebou nikdy nebude platit současně.
Nyní se zbytečně projíždí mnoho IFů, i když už je jasné, že platil třeba ten první...

Čím chráníš ten LED pásek? Nějaká pevná průhledná plastová trubice? Děti meče zrovna nešetří :lol:

jankop
Příspěvky: 1029
Registrován: 06 zář 2017, 20:04
Reputation: 0
Bydliště: Brno
Kontaktovat uživatele:

Re: WS2812 a MPU6050 jako "světelný meč"

Příspěvek od jankop » 06 pro 2022, 19:21

Nechráním ho nijak, je to jen funkční prototyp nalepený na laťce. Mechanické provedení je na kamarádovi, pro kterého jsem to programoval.

KamilV
Příspěvky: 479
Registrován: 03 dub 2018, 15:27
Reputation: 0
Bydliště: Olomouc

Re: WS2812 a MPU6050 jako "světelný meč"

Příspěvek od KamilV » 06 pro 2022, 20:38

Kdyby chtěl tip, tak tohle nevychází draze.
https://www.buildex.cz/plexi-trubka-evo ... barva-cira

jankop
Příspěvky: 1029
Registrován: 06 zář 2017, 20:04
Reputation: 0
Bydliště: Brno
Kontaktovat uživatele:

Re: WS2812 a MPU6050 jako "světelný meč"

Příspěvek od jankop » 06 pro 2022, 20:46

To je dobrá cena.

ondraN
Příspěvky: 932
Registrován: 08 srp 2019, 20:01
Reputation: 0

Re: WS2812 a MPU6050 jako "světelný meč"

Příspěvek od ondraN » 09 pro 2022, 17:55

Tak mě napadlo, což dát ty hodnoty z akcelerometru do registrů jednotlivých ledek. Meč by tak při prudkém pohybu jasně zazářil a podle směru i různou barvou. To by děti řičeli blahem :mrgreen:

jankop
Příspěvky: 1029
Registrován: 06 zář 2017, 20:04
Reputation: 0
Bydliště: Brno
Kontaktovat uživatele:

Re: WS2812 a MPU6050 jako "světelný meč"

Příspěvek od jankop » 10 pro 2022, 14:15

I to jsem zkoušel, změnu barvy podle rychlosti a polohy meče, ta možná variabilita je obrovská. Ale mnoho psů, zajícova smrt, děti můžeš taky znudit. Myslím, že ten duhový efekt je nepřekonatelný.

jankop
Příspěvky: 1029
Registrován: 06 zář 2017, 20:04
Reputation: 0
Bydliště: Brno
Kontaktovat uživatele:

Re: WS2812 a MPU6050 jako "světelný meč"

Příspěvek od jankop » 16 pro 2022, 13:46

Ještě připojuji neumělé video
https://fancon.cz/common/sword.mp4

jankop
Příspěvky: 1029
Registrován: 06 zář 2017, 20:04
Reputation: 0
Bydliště: Brno
Kontaktovat uživatele:

Re: WS2812 a MPU6050 jako "světelný meč"

Příspěvek od jankop » 24 pro 2022, 16:24

WS2812 moc pěkně svítí. Naprogramoval jsem v tomhle čase "Východ slunce nad Betlémem". Ještě bych chtěl udělat cca dva volitelné programy. "Přelet mezinárodní orbitální stanice ISS nad Betlémem" a "Já jsem ti Josefe říkala, ať neházíš ty nedopalky do sena!" :D
https://fancon.cz/common/betlem.mp4
V reálu je to mnohem pěknější. Zezadu je přidělané Arduino Nano, které řídí 2 x 8 WS2812

V každém případě Vám všem přeji krásné Vánoce a celý nastávající rok.

Odpovědět

Kdo je online

Uživatelé prohlížející si toto fórum: Žádní registrovaní uživatelé a 6 hostů