بازی با آتش توسط وایفای، ESP8266 و نئوپیکسل [شامل اپلیکیشن اندروید]

فهرست مطالب

مقدمه

به کمک ارتباط بی‌سیم WiFi یک شبیه ساز آتش بسازید! یک اپلیکیشن تلفن همراه (برای گوشی‌های هوشمند اندروید) با رابطه کاربری جذاب آماده است تا به کمک آن ساخته‌ی خود را کنترل کنید. همجنین از آردینو و ESP8266 برای کنترل شعله آتش استفاده می‌کنیم.

آن چه در این آموزش یاد می‌گیرید

آشنایی با نئوپیکسل

نئوپیکسل‌ها مدت زیادی است که در بازار هستند و شما احتمالا آن‌ها را می‌شناسید. اما اگر نمی‌شناسید نئوپیکسل‌ها مانند ال ای دی ‌های RGB هستند اما با این تفاوت که هر هر ال ای دی به طور جداگانه قابل تنظیم می‌باشد. این قابلیت باعث می‌شود که بتوان هزاران الگوی جالب به کمک نئوپیکسل‌ها ایجاد کرد. برای WS2812b شما فقط به 3 سیم نیاز دارید، 2  سیم برای تغذیه و 1 سیم برای داده. این به این معنی است که شما فقط با یک پین  آردوینو می‌توانید تعداد زیادی ال ای دی را کنترل کنید.

در این پروژه ما قصد داریم از این LED های هوشمند برای ایجاد یک شبیه ساز آتش استفاده کنیم. برای کنترل LED ها  از کتابخانه FastLED  استفاده می‌کنیم. با مثال Fire2012 از کتابخانه ای که توسط مارک کریگسمن نوشته شده است شروع می‌کنیم. از 6 نوار LED که هر کدام دارای 30 LED (مجموع 180 LED) است استفاده می کنیم. 6 نوار LED که هر کدام دارای 30 LED (مجموع 180 LED) است را بر روی یک قطعه لوله PVC قرار داده و آنها را در یک سیلندر شیشه قرار می دهیم (این سیلندرهای شیشه ای معمولا به عنوان گلدان ها استفاده می شود). ما باید نور LED ها را پخش کنیم تا آنها را مداوم ببینیم، برای این کار از کاغذ ردیابی استفاده کردیم که نور را عبور داده و منتشر می‌کند.

لوازمی که با آن احتیاج دارید

قطعات مورد نیاز

برد توسعه Witty cloud با هسته وایفای ESP8266 × 1
نوار RGB LED دیجیتال 5 متری NeoPixel × 1
مبدل منطقی ( لاجیک کانورتر ) دو طرفه با قابلیت تبدیل 5 به 3.3 ولت و برعکس × 1
سیم جامپر نری به مادگی × 1
PVC pipe 60cm size 2” × 1
Tracing paper × 1
Glass cylinder × 1

نرم افزار‌های مورد نیاز

Arduino IDE

ابزارهای مورد نیاز

چسب حرارتی
هویه

مراحل ساخت

ابتدا یک سیلندر شیشه ای درست کنید. سیلندر ما 60cm طول و 12cm قطردارد. بهتر است از سیلندر شیشه‌ای مات استفاده کنید. اما اگر یک شیشه ی شفاف دارید، می توانید کاغذ ردیابی را برای پوشش سطح سیلندر (سطح داخلی یا خارجی) استفاده کنید. کاغذ ردیابی انتشار نور را به خوبی انجام می‌دهد. سپس طول داخلی سیلندر شیشهای را اندازه بگیرید و لوله PVC را به گونه‌ای ببرید که در داخل سیلندر جا شود. سیلندر شیشه‌ای ما 60cm طول دارد( بدون احتساب پایه طول داخلی آن 59cm است). پس لوله PVC را به اندازه 59cm می‌بریم.نوار های LED را در این لوله بچسبانید. یک لوله با قطر 4cm عالی خواهد‌بود. سپس باید نوار LED را به 6 قسمت مساوی تقسیم کنیم. ما در اینجا از نوار 60LEDs/m استفاده کرده‌ایم.(شما می‌توانید از نوار با چگالی بالاتر استفاده کنید تا نتیجه بهتری بگیرید.)ما به 50 طول 6 سانتی متری  استفاده میکنیم، به این معنی که به 3 متر طول نیاز داریم. شش نوار را به طور مساوی اطراف لوله پی وی سی جایگذاری کنید. در نهایت باید چیزی شبیه به این داشته‌باشید:

برای اتصال نوارهای LED به هم می توانید یا به طور مستقیم نوارها را مانند شکل زیر یه هم لحیم کنید، یا ابتدا به نوار‌ها پین هدر لحیم کنید و سپس به کمک یسم برد بورد آن‌ها را به هم متصل کنید.

هنگامی که اتصال نوار LED انجام شد، باید لوله را داخل سیلندر قرار دهید. می‌توانید از فوم  برای برش یک دایره ای که دارای قطر بیرونی برابر قطر داخلی سیلندر شیشه ای است و قطر داخلی آن برابر قطر خارجی لوله PVC باشد استفاده کنید. 2 عدد از این را برای هر طرف لوله آماده کنید. این قطعات را به انتهای لوله اضافه کنید و به آرامی لوله را داخل سیلندر قرار دهید.

کد

برای کدنویسی و آپلود آن روی برد از Arduino IDE استفاده میکنیم. اگر می‌خواهید فایل‌های نرم افزاری کنترلر را روی SPIFFS آپاود کنید، باید از بردی استفاده کنید که یک ESP8266 با 3MB از SPIFFS داشته باشد. شما می‌توانید فایل‌های کنترلر را روی SPIFFS که مخفف “Serial Peripheral Interface Flash File System” می‌باشد آپلود کنید تا در ادامه فایل‌ها را از آن محل اجرا کنید. با انجام این کار می توانید مرورگر خود را (یا بر روی گوشی یا نوت بوک خود) باز کنید و آدرس ESP خود را وارد کنید (به طور پیش فرض 192.168.4.1 است) و  رابط کنترل را در مرورگر خود بدون نیاز به نصب برنامه دریافت کنید، اگر شما آیفون یا iPad دارید این تنها انتخاب شماست.

کد زیر را روی برد ESP خود آپلود کنید. برای این کار به کتابخانه FastLED نیاز دارید، بنابراین ابتدا آن را به IDE Arduino خود اضافه کنید.(شما می توانید آن را اینجا دانلود کنید). کد شبیه سازی آتش، طرح Score2012 مارک Kriegsman است که می‌توانید آن را در مثال‌ها ببینید. این مثال برای یک نوار LED نوشته شده است، اما در اینجا ما کد را برای استفاده از تعداد متغیر نوارها اصلاح کردیم. هر چه تعداد نوار‌ها بیشتر باشد نتیجه‌ی بهتری خواهید داشت. منطق شبیه سازی آتش به وضوح در فایل مثال نشان داده شده است.اگر می خواهید بدانید که این کد چگونه کار می کند، کد منبع این مثال را مطالعه کنید.

فایل‌های مورد نیاز:

#include "ESP8266WiFi.h"
#include "ESP8266WebServer.h"
#include "FastLED.h"
#include "EEPROM.h"
#include "FS.h"  //required for SPIFFS

#define DATA_PIN 5
#define LED_TYPE WS2811
#define COLOR_ORDER GRB
#define NUM_LEDS 30
#define NUM_STRIPS 6
#define CHIPSET     WS2812B

//addresses to save data to EEPROM to preserve the state of fire simulation
#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 //if this value is 250 we assume we have previously saved to EEPROM and load data from that

CRGB leds[NUM_STRIPS * NUM_LEDS];

String inData;
uint8_t FPS = 100; //FRAMES_PER_SECOND
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;            //if true we go to save to EEprom.
unsigned long changeMillis = 0; //changes will be saved 1 minute after no change is applied to avoid EEPROM wear.

bool initSetup = true;


CRGBPalette16 gPal;

ESP8266WebServer server(80); //Web server object. Will be listening in port 80 (default for HTTP)

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); // sanity delay
  FastLED.addLeds<CHIPSET, DATA_PIN, COLOR_ORDER>(leds, NUM_STRIPS * NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
}

void loop()
{

  server.handleClient(); //Handling of incoming requests
  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];

  // Step 1.  Cool down every cell a little
    for( int i = 0; i < NUM_LEDS; i++) { heat[stripNo][i] = qsub8( heat[stripNo][i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2)); }
    // Step 2. Heat from each cell drifts 'up' and diffuses a little
    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;
    }
    
    // Step 3.  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int y = random8(5);
      heat[stripNo][y] = qadd8( heat[stripNo][y], random8(160,200) );
    }

    // Step 4.  Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      // Scale the heat value from 0-255 down to 0-240
      // for best results with color palettes.
      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)
  {
    // save the last time you blinked the LED
    previousMillis = currentMillis;
    return true;
  }
  else
  {
    return false;
  }
}

void EEPROMupdate(byte address, byte value)
{
  if (EEPROM.read(address) != value)
  {
    EEPROM.write(address, value);
    EEPROM.commit();
  }
  return;
}

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", ""); //Returns the HTTP response
}

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", ""); //set a password here if you want i.e. WiFi.softAP("ElectroPeak's Flame", "12345678");

  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(); //Start the server
}
برای کنترل “look and feel” دو متغیر در دسترس است، SPARKING و COOLING که می توانید بصورت پویا آن ها را در نرم افزار آپلود شده در SPIFFS یا اپلیکیشن اندروید، کنترل کنید. همچنین FPS را اینجا نیز می توانید کنترل کنید.
رنگ آتش با یک پالت رنگ که در نرم افزار قابل تغییر است، کنترل می شود. کافیست برای تغییر رنگ، رنگ مورد نظرتان را در پالت انتخاب کنید. بعد از تنظیم رنگ صفحه را ببندید و تغییر را مشاهده کنید.

چگونه روی SPIFFS آپلود کنیم؟

برای آپلود فایل در حافظه SPIFFS با استفاده از IDE آردوینو، ابتدا باید فولدر برنامه یک پوشه بنام data بسازید و تمام فایل هایی که برای آپلود کردن مدنظر دارید، داخل آن پوشه کپی کنید. سپس برای IDE خود نیاز به افزونه Arduino ESP8266 filesystem uploader دارید. طبق دستورالعمل Github افزونه را نصب کنید، بعد از اتمام نصب در منوی tools گزینه ESP8266 Sketch Data Upload در دسترس خواهد بود. ماژول ESP خود را برای برنامه ریزی آماده کنید و منتظر بمانید فایل ها بطور کامل آپلود شوند. سرعت آپلود را روی 921600 تنظیم کنید تا انتقال سریعتر انجام شود.

شبیه ساز آتش چگونه کار می کند؟

برنامه آپلود شده روی ESP8266 یک وب سرور روی سیستم ایجاد می کند که به درخواست ارسال شده از اپلیکیشن، پاسخ می دهد. اپلیکیشن به سادگی درخواست های GET را بع سرور(ESP8266) ارسال می کند. اطلاعات رنگ برای ساخت پالت بعنوان یک آرگومان در درخواست GET ارسال می شود، برای پارامترهای دیگر نظیر SPARKING و COOLING نیز همین روند تکرار می شود.

برای مثال برای تنظیم شدت روشنایی، درخواست زیر توسط اپلیکیشن ارسال می شود:

http://192.168.4.1/conf?brightness=224

در برنامه آردوینو الگوریتمی وجود دارد که وقتی درخواست بالا دریافت شد، شدت روشنایی را متناسب با درخواست تغییر دهد. برای اطلاعات بیشتر کد را مطالعه کنید.

اپلیکیشن اندروید

اپلیکیشن اندروید با استفاده از Phonegap طراحی شده که به شما اجازه می دهد اپلیکیشن های موبایلی را با تکنولوژی های وب(HTML, CSS, Javascript) طراحی کنید.

می توانید سورس کد این اپلیکیشن را از لینک زیر دانلود کنید:

از این مطلب خوشتان آمد؟

آخرین مطالت و آموزش ها را در ایمیلتان دریافت کنید:

مطالب بیشتر

Comments (3)

  • علی سلیمیان Reply

    سلام خسته نباشید من این ارور را دارم میشه راهنمایی کنید؟

    Arduino: 1.8.13 (Windows 10), Board: “Generic ESP8285 Module, 80 MHz, Flash, Legacy (new can return nullptr), All SSL ciphers (most compatible), dtr (aka nodemcu), 26 MHz, 1MB (FS:64KB OTA:~470KB), 2, v2 Lower Memory, Disabled, None, Only Sketch, 115200”

    In file included from C:\Users\Ali\AppData\Local\Temp\arduino_modified_sketch_303853\BasicHttpClient.ino:3:0:

    C:\Users\Ali\Documents\Arduino\libraries\FastLED\src/FastLED.h:14:21: note: #pragma message: FastLED version 3.004.000

    # pragma message “FastLED version 3.004.000”

    ^

    In file included from C:\Users\Ali\Documents\Arduino\libraries\FastLED\src/FastLED.h:65:0,

    from C:\Users\Ali\AppData\Local\Temp\arduino_modified_sketch_303853\BasicHttpClient.ino:3:

    C:\Users\Ali\Documents\Arduino\libraries\FastLED\src/fastspi.h:135:23: note: #pragma message: No hardware SPI pins defined. All SPI access will default to bitbanged output

    # pragma message “No hardware SPI pins defined. All SPI access will default to bitbanged output”

    ^

    C:\Users\Ali\AppData\Local\Temp\arduino_modified_sketch_303853\BasicHttpClient.ino: In function ‘void setup()’:

    BasicHttpClient:51:3: error: ‘c’ was not declared in this scope

    c.WiFi();

    ^

    BasicHttpClient:53:14: error: ‘loadConfig’ was not declared in this scope

    loadConfig();

    ^

    C:\Users\Ali\AppData\Local\Temp\arduino_modified_sketch_303853\BasicHttpClient.ino: In function ‘void loop()’:

    BasicHttpClient:77:20: error: ‘saveToEEPROM’ was not declared in this scope

    saveToEEPROM();

    ^

    BasicHttpClient:80:8: error: ‘fire’ was not declared in this scope

    fire();

    ^

    C:\Users\Ali\AppData\Local\Temp\arduino_modified_sketch_303853\BasicHttpClient.ino: In function ‘void Fire2012WithPalette(int)’:

    BasicHttpClient:92:21: error: ‘k’ was not declared in this scope

    heat[stripNo][k] = (heat[stripNo][k – 1] + heat[stripNo][k – 2] + heat[stripNo][k – 2] ) / 3;

    ^

    C:\Users\Ali\AppData\Local\Temp\arduino_modified_sketch_303853\BasicHttpClient.ino: At global scope:

    BasicHttpClient:96:5: error: expected unqualified-id before ‘if’

    if( random8() < SPARKING ) {

    ^

    BasicHttpClient:102:5: error: expected unqualified-id before 'for'

    for( int j = 0; j < NUM_LEDS; j++) {

    ^

    BasicHttpClient:102:21: error: 'j' does not name a type

    for( int j = 0; j < NUM_LEDS; j++) {

    ^

    BasicHttpClient:102:35: error: 'j' does not name a type

    for( int j = 0; j Preferences.

    فوریه 24, 2021 at 6:11 ب.ظ
  • سامان Reply

    سلام این کد ها را میشه با گوشی وارد کرد یا فقط سیستم می‌تواند کد را اجرا کند اگر امکان اجرا کد با گوشی امکان پذیر است لطفا راهنمایی کنید ممنون لطفا

    مارس 24, 2024 at 9:38 ب.ظ
    • محمد دمیرچی Reply

      با سلام
      در صورتی که منظور شما از پروگرام کردن برد می باشد، یکسری برنامه اندرویدی برای این قضیه وجود دارد ولی اکثرا به دلیل محدودیت هایی که دارند پیشنهاد نمیکنم و بهتر از یک سیستم برای این کار استفاده کنید.
      اگر منظور شما از صفحه وبی که در آن تغییرات داده میشود را مد نظر دارید، فرقی نمیکند که چه گوشی باشد یا سیستم فقط کافی است صفحه مورد نظر را داخل مرورگر مورد نظر خود باز کنید.

      مارس 25, 2024 at 9:53 ق.ظ

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد.