NeoPixel LED-strip via Wi-Fi besturen: ESP8266-project met Android-app
Onderdelen en benodigdheden
| ElectroPeak ESP8266 Serial WIFI Witty Cloud Board | 1 |
| NeoPixel Slimme LED-strip (60 LED/m) | 3 meter (6 stukken van 50 cm) |
| ElectroPeak Logic Level Converter | 1 |
| ElectroPeak 21cm 40P Male-Female Draadjes | 1 |
| PVC-buis 60cm, 2 inch | 1 |
| Calqueerpapier | 1 |
| Glascilinder | 1 |
| 5V voeding (minimaal 9A) | 1 |
| Arduino Nano R3 | 1 |
Benodigde tools en machines
- Lijmpistool
- Solderbout
Apps en online diensten
- Arduino IDE
Over dit project
Je kunt deze en andere geweldige tutorials lezen op de officiële website van ElectroPeak.
Creëer een cool vuursimulatie-effect met draadloze Wi-Fi-besturing. Een mobiele app (voor Android) met een mooie interface is klaar om te installeren en te spelen met je creatie! We gebruiken Arduino en ESP8266 om de vlam te besturen. Aan het einde van dit project leer je:
- Hoe NeoPixels werken.
- Hoe je de ESP8266 programmeert en variabelen via Wi-Fi besteurt.
- Hoe je een cool vuureffect maakt met NeoPixels.
Inleiding tot NeoPixels
Individueel adresseerbare LEDs, vaak NeoPixels genoemd, bestaan al een tijdje. Als je ze niet kent: ze zijn als normale RGB-leds, maar de kleur van elke individuele LED kan apart worden aangestuurd, wat oneindig coole patronen en animaties mogelijk maakt. Voor de WS2812b heb je slechts 3 draden nodig: 2 voor voeding en 1 voor data. Dat betekent dat je slechts één vrije Arduino-pin nodig hebt om een heleboel LEDs aan te sturen!
In dit project gebruiken we deze slimme LEDs voor een vuureffect. Voor het aansturen gebruiken we de geweldige FastLED-bibliotheek. We gebruiken de Fire2012-schets uit de voorbeelden, geschreven door Mark Kriegsman. We gebruiken 6 LED-strips van elk 30 LEDs (totaal 180 LEDs), die we op een stuk PVC-buis plakken en in een glazen cylinder plaatsen (deze cylinders worden meestal als vaas gebruikt). We moeten het licht van de LEDs diffuseren om ze continu te laten lijken; daarvoor gebruiken we calqueerpapier dat licht doorlaat en diffuseert.

Constructie
Eerst een geschikte glazen cylinder zoeken; de onze is 60 cm lang en 12 cm diameter.
Als je een gematte glazen cylinder vindt, is dat mooi, maar bij helder glas kun je calqueerpapier op het oppervlak (binnen of buiten) plakken; calqueerpapier diffuseert het licht goed en geeft mooie resultaten.
Meet de interne lengte van de cylinder en zaag de PVC-buis op maat. Onze cylinder heeft een hoogte van 60 cm (interne lengte 59 cm), dus de PVC-buis is 59 cm. Een buis met 4 cm diameter is perfect.
Knip de LED-strip in 6 gelijke delen. We gebruiken 60 LEDs/m dichtheid (je kunt hogere dichtheden gebruiken voor betere effecten). We gebruiken zes stukken van 50 cm, dus in totaal 3 meter. Verdeel de zes stukken gelijkmatig om de PVC-buis en plak de strips vast. Zo ziet het eruit:





Om de LED-strips te verbinden kun je ofwel direct draden solderen volgens het schema, ofwel eerst pinheaders solderen en daarna breadboard-draden gebruiken.

Wanneer alle verbindingen klaar zijn, plaats je de buis in de cylinder. Om de buis gecentreerd te houden, knip je uit schuim een cirkel met een buitendiameter gelijk aan de binnen-diameter van de glazen cylinder en een binnen-diameter gelijk aan de buitendiameter van de PVC-buis. Maak er twee (voor boven en onder). Bevestig ze aan de uiteinden en duw de buis voorzichtig in de cylinder.
Code
We gebruiken de Arduino IDE voor coderen en uploaden naar de ESP8266. Je hebt een bord nodig met een ESP8266 met 3 MB SPIFFS als je de controller-bestanden op de SPIFFS wilt uploaden. SPIFFS staat voor "Serial Peripheral Interface Flash File System"; je kunt de controller-bestanden daar uploaden om ze vanaf die locatie te serveren. Door dit te doen kun je je browser (op telefoon of laptop) openen en naar het adres van je ESP gaan (standaard 192.168.4.1) en krijg je de controller-interface in je browser zonder de app te hoeven installeren; als je een iPhone of iPad hebt, is dit je enige keuze.
Upload de volgende sketch naar je ESP-bord. We hebben de FastLED-bibliotheek nodig, dus voeg die toe aan je Arduino IDE als je dat nog niet gedaan hebt (je kunt het hier downloaden). De vuursimulatie-code is Mark Kriegsmans fire2012-schets uit de voorbeelden. Die is voor één strip, maar hier hebben we de code aangepast voor een variabel aantal strips. Hoe meer strips/LEDs, hoe groter het effect.
De logica van de vuursimulatie is duidelijk beschreven in het voorbeeldbestand. Als je wilt weten hoe het werkt, lees de broncode van het voorbeeld.
#include
#include
#include "FastLED.h"
#include "EEPROM.h"
#include "FS.h" // vereist voor SPIFFS
#define DATA_PIN 5
#define LED_TYPE WS2811
#define COLOR_ORDER GRB
#define NUM_LEDS 30
#define NUM_STRIPS 6
#define CHIPSET WS2812B
// Adressen om data in EEPROM op te slaan (state van vuursimulatie behouden)
#define cs0Adr 0
#define cs1Adr 3
#define cs2Adr 6
#define cs3Adr 9
#define BriAdr 15
#define FpsAdr 16
#define SparkingAdr 17
#define CoolingAdr 18
#define EEPROMCheckAdr 20 // als deze waarde 250 is, gaan we ervan uit dat we eerder naar EEPROM hebben geschreven en laden data daarvandaan
CRGB leds[NUM_STRIPS * NUM_LEDS];
String inData;
uint8_t FPS = 100;
uint8_t SPARKING = 150;
uint8_t COOLING = 90;
uint8_t BRIGHTNESS = 100;
uint8_t csRGB[4][3] = {{0, 0, 0}, {255, 0, 0}, {255, 127, 0}, {255, 255, 255}};
unsigned long previousMillis = 0;
bool change = false;
unsigned long changeMillis = 0;
bool initSetup = true;
CRGBPalette16 gPal;
ESP8266WebServer server(80);
void setup() {
EEPROM.begin(200);
cWiFi();
setupFastLED();
loadConfig();
gPal = CRGBPalette16(
CRGB(csRGB[0][0], csRGB[0][1], csRGB[0][2]),
CRGB(csRGB[1][0], csRGB[1][1], csRGB[1][2]),
CRGB(csRGB[2][0], csRGB[2][1], csRGB[2][2]),
CRGB(csRGB[3][0], csRGB[3][1], csRGB[3][2])
);
}
inline void setupFastLED() {
delay(1000);
FastLED.addLeds(leds, NUM_STRIPS * NUM_LEDS).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(BRIGHTNESS);
}
void loop() {
server.handleClient();
if (change) {
if (millis() - changeMillis > 60000) {
change = false;
saveToEEPROM();
}
}
fire();
FastLED.show();
FastLED.delay(1000 / FPS);
}
void Fire2012WithPalette(int stripNo) {
static byte heat[NUM_STRIPS][NUM_LEDS];
// Stap 1: Afkoelen
for (int i = 0; i < NUM_LEDS; i++) {
heat[stripNo][i] = qsub8(heat[stripNo][i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
}
// Stap 2: Warmte drift opwaarts
for (int k = NUM_LEDS - 1; k >= 2; k--) {
heat[stripNo][k] = (heat[stripNo][k - 1] + heat[stripNo][k - 2] + heat[stripNo][k - 2]) / 3;
}
// Stap 3: Willekeurige vonkjes
if (random8() < SPARKING) {
int y = random8(5);
heat[stripNo][y] = qadd8(heat[stripNo][y], random8(160, 200));
}
// Stap 4: Warmte naar kleuren mappen
for (int j = 0; j < NUM_LEDS; j++) {
byte colorindex = scale8(heat[stripNo][j], 240);
CRGB color = ColorFromPalette(gPal, colorindex);
leds[j + stripNo * NUM_LEDS] = color;
}
}
void fire() {
for (int i = 0; i < NUM_STRIPS; i++) {
Fire2012WithPalette(i);
}
}
int str2int(String InputStr) {
return InputStr.toInt();
}
boolean EveryNSec(uint8_t period) {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= period * 1000) {
previousMillis = currentMillis;
return true;
}
return false;
}
void EEPROMupdate(byte address, byte value) {
if (EEPROM.read(address) != value) {
EEPROM.write(address, value);
EEPROM.commit();
}
}
void saveToEEPROM() {
EEPROMupdate(BriAdr, BRIGHTNESS);
EEPROMupdate(FpsAdr, FPS);
EEPROMupdate(SparkingAdr, SPARKING);
EEPROMupdate(CoolingAdr, COOLING);
for (uint8_t i = 0; i < 4; i++) {
for (uint8_t j = 0; j < 3; j++) {
EEPROMupdate((i * 3 + j), csRGB[i][j]);
}
}
}
void handleCS0Change() {
csRGB[0][0] = str2int(server.arg("R"));
csRGB[0][1] = str2int(server.arg("G"));
csRGB[0][2] = str2int(server.arg("B"));
gPal = CRGBPalette16(
CRGB(csRGB[0][0], csRGB[0][1], csRGB[0][2]),
CRGB(csRGB[1][0], csRGB[1][1], csRGB[1][2]),
CRGB(csRGB[2][0], csRGB[2][1], csRGB[2][2]),
CRGB(csRGB[3][0], csRGB[3][1], csRGB[3][2])
);
changeMillis = millis();
change = true;
}
void handleCS1Change() {
csRGB[1][0] = str2int(server.arg("R"));
csRGB[1][1] = str2int(server.arg("G"));
csRGB[1][2] = str2int(server.arg("B"));
gPal = CRGBPalette16(
CRGB(csRGB[0][0], csRGB[0][1], csRGB[0][2]),
CRGB(csRGB[1][0], csRGB[1][1], csRGB[1][2]),
CRGB(csRGB[2][0], csRGB[2][1], csRGB[2][2]),
CRGB(csRGB[3][0], csRGB[3][1], csRGB[3][2])
);
changeMillis = millis();
change = true;
}
void handleCS2Change() {
csRGB[2][0] = str2int(server.arg("R"));
csRGB[2][1] = str2int(server.arg("G"));
csRGB[2][2] = str2int(server.arg("B"));
gPal = CRGBPalette16(
CRGB(csRGB[0][0], csRGB[0][1], csRGB[0][2]),
CRGB(csRGB[1][0], csRGB[1][1], csRGB[1][2]),
CRGB(csRGB[2][0], csRGB[2][1], csRGB[2][2]),
CRGB(csRGB[3][0], csRGB[3][1], csRGB[3][2])
);
changeMillis = millis();
change = true;
}
void handleCS3Change() {
csRGB[3][0] = str2int(server.arg("R"));
csRGB[3][1] = str2int(server.arg("G"));
csRGB[3][2] = str2int(server.arg("B"));
gPal = CRGBPalette16(
CRGB(csRGB[0][0], csRGB[0][1], csRGB[0][2]),
CRGB(csRGB[1][0], csRGB[1][1], csRGB[1][2]),
CRGB(csRGB[2][0], csRGB[2][1], csRGB[2][2]),
CRGB(csRGB[3][0], csRGB[3][1], csRGB[3][2])
);
changeMillis = millis();
change = true;
}
void handleConf() {
if (server.arg("brightness") != "") {
BRIGHTNESS = str2int(server.arg("brightness"));
FastLED.setBrightness(BRIGHTNESS);
changeMillis = millis();
change = true;
}
if (server.arg("fps") != "") {
FPS = str2int(server.arg("fps"));
changeMillis = millis();
change = true;
}
if (server.arg("sparking") != "") {
SPARKING = str2int(server.arg("sparking"));
changeMillis = millis();
change = true;
}
if (server.arg("cooling") != "") {
COOLING = str2int(server.arg("cooling"));
changeMillis = millis();
change = true;
}
server.sendHeader("Connection", "close");
server.sendHeader("Access-Control-Allow-Origin", "*");
server.send(200, "text/plain", "");
}
void loadConfig() {
if (EEPROM.read(EEPROMCheckAdr) == 250) {
BRIGHTNESS = EEPROM.read(BriAdr);
SPARKING = EEPROM.read(SparkingAdr);
COOLING = EEPROM.read(CoolingAdr);
FPS = EEPROM.read(FpsAdr);
if (FPS == 0) FPS = 100;
for (uint8_t i = 0; i < 4; i++) {
for (uint8_t j = 0; j < 3; j++) {
csRGB[i][j] = EEPROM.read(i * 3 + j);
}
}
} else {
EEPROMupdate(BriAdr, BRIGHTNESS);
EEPROMupdate(FpsAdr, FPS);
EEPROMupdate(CoolingAdr, COOLING);
EEPROMupdate(SparkingAdr, SPARKING);
for (uint8_t i = 0; i < 4; i++) {
for (uint8_t j = 0; j < 3; j++) {
EEPROMupdate((i * 3 + j), csRGB[i][j]);
}
}
EEPROMupdate(EEPROMCheckAdr, 250);
}
}
void cWiFi() {
WiFi.softAP("ElectroPeak's Flame", "");
IPAddress myIP = WiFi.softAPIP();
server.on("/cs0", handleCS0Change);
server.on("/cs1", handleCS1Change);
server.on("/cs2", handleCS2Change);
server.on("/cs3", handleCS3Change);
server.on("/conf", handleConf);
server.serveStatic("/", SPIFFS, "/", "max-age=86400");
server.begin();
}
Om het "uiterlijk en gevoel" van het vuur te regelen, zijn er twee variabelen: SPARKING en COOLING, die je dynamisch kunt aanpassen via de controller-software (op SPIFFS) of de Android-app. Je kunt ook FPS instellen.

De kleur van het vuur wordt bestuurd door een kleurenpalet, wat ook wijzigbaar is via de controller-software (via 4 kleurstops). Klik/tap op elk kleurcirkeltje voor een kleurstop om de kleur in te stellen, klik daarna op sluiten om de verandering te zien.

Hoe uploaden naar SPIFFS?
Om bestanden naar de SPIFFS-geheugen te uploaden via de Arduino IDE, maak eerst een map "data" in de schetsmap en plaats alle te uploaden bestanden daarin. Het hier geüploade zip-bestand bevat zowel de schets als deze map.
Vervolgens heb je de Arduino ESP8266 filesystem uploader plugin nodig. Volg de instructies op de GitHub-pagina en installeer de plugin. Na installatie vind je ESP8266 Sketch Data Upload onder het menu Tools. Zet je ESP in programmeermodus en klik daarop. Wees geduldig; het uploaden kan even duren. Tip: stel "upload speed" in op 921600 voor snellere upload.

Hoe werkt het?
De sketch op de ESP8266 creeërt een webserver die reageert op verzoeken van de app. De app stuurt GET-verzoeken naar de server (ESP8266). De kleurdata voor het palet worden als argumenten in het GET-verzoek meegestuurd, net als andere parameters zoals Sparking en Cooling.
Bijvoorbeeld, om de helderheid in te stellen, stuurt de app:
https://192.168.4.1/conf?brightness=224
Er is een handler in de sketch die bij ontvangst van dit verzoek de helderheid instelt. Bekijk de code voor meer details.
Android-app
De Android-app is gemaakt met Phonegap, een technologie voor cross-platform mobiele apps met webtechnologieën (HTML, CSS, JavaScript). De broncode vind je op deze pagina.
Lees deze en andere geweldige tutorials op de officiële website van ElectroPeak.
Productieproces
- Een Snel, Betrouwbare Brandsdetectiesysteem Bouwen met Samsung SAMIIO, Arduino UNO & Raspberry Pi
- Een Budget-vriendelijke Raspberry Pi Babyhuil-detector Bouwen – DIY Handleiding
- Slimme personnencounter met Android-app: Lasers en LDR's
- Een DFRobot Capacitieve Vingerafdruksensor integreren met Arduino of ESP8266
- Bestuur Je Roomba Create 2 Met Arduino En Android: Stapsgewijze Handleiding
- Axiom Equipment Group en gemeenschapssponsors doneren $11.000 aan Oxbow Brandweer
- IoT benutten voor vroege bosbranddetectie en -preventie
- Brandweer Revolutie: Hoe IoT Veiligheid, Respons En Redding Verbetert
- iOS vs. Android: Welk platform biedt superieure enterprise app-beveiliging?
- Brandschade en Preventie: Expertsstrategieën om Levens en Goederen te Beschermen
- Python's strip()-methode beheersen: Uitgebreide handleiding & praktische voorbeelden