数码之家

 找回密码
 立即注册
搜索
查看: 19747|回复: 35

[Arduino] ESP8266 安卓APP自定义控制WS2812B彩灯串(DIY系列一)

    [复制链接]
发表于 2019-12-20 09:49:41 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

您需要 登录 才可以下载或查看,没有账号?立即注册

x
最近在GitHub捣鼓了一个ESP8266控制WS2812B彩灯,表白或炫技的神器,分享出来给有兴趣的坛友。

一.硬件部分

1. ESP8266开发板:
NodeMCU1.0是一个开源的搭载ESP8266wifi芯片的ESP-12F模组,集合CP2102,USB转串口的开发板,网上价格极低,深受电子DIYer喜爱 。当然也可以使用其它采用ESP8266主控的开发板。
th.jpg

动手能力好的同学也可以自己制作,附上PCB文件:
NODEMCU_DEVKIT_V1.0_GERBER_V4.rar (228.68 KB, 下载次数: 56)


2. WS2812B灯带:
WS2812B是一颗现在大量使用的景观灯,氛围灯灯带的集成控制功能的LED灯珠,具有16777216种颜色的全真色彩显示,单线通讯,一进一出方便级联。
dddd.jpg
dd.jpg
ddddddd.jpg

网上很多灯带或者是矩阵或者圆形PCB成品出售,可以按喜好选择
1586.jpg

我使用的是一个开源的硬件,60灯的圆形灯串,它由4个弧形PCB级联而成。自己打PCB手工焊接,有些难度,但好在这个灯珠比较耐焊,焊好一次成功,没有损坏。
1768-00.jpg leds_ring60.jpg 1768-02.jpg


P91220-101817.jpg
P91220-101932.jpg

同样附上PCB文件:
Adafruit-NeoPixel-Ring-master.zip (1.43 MB, 下载次数: 45)


3. 0.96寸OLED显示屏
Waveshare-0.96-OLED-B-SSD1306-Display-Screen-Module-Deep-Blue.jpg

网上购买,使用ssd1306主控,分辨率128*64,I2C接口

4. 接线方法:
QQ截图20191220105141.jpg

注意:图中WS2812灯串直接接在了开发板3.3V上,测试发现60灯以内电流是OK的,可以正常工作。如果你的灯串数量更多,请单独增加一个电源,或者使用7805降压给开发板和灯串供电,电流不够会引起数据异常,灯串显示不正常。




二.软件部分

1. Arduino IDE工具搭建ESP8266平台
使用Arduino IDE工具ESP8266平台开发,想要仿制的入门级坛友,需要首先搭建好平台,具体可参考相关帖子或使用搜索引擎。

Arduino IDE工具,最新版本1.8.10

软件

软件


搭建好ESP8266平台,选择NodeMCU1.0(ESP-12E Module),其它按默认配置

8266

8266



相关代码及库文件:

配置好软件后在《工具》《管理库》的库管理器中搜索安装以下库文件:
图片.png

WS2812FX
WiFiManager
ArduinoOTA
ArduinoJson
Adafruit_GFX
Adafruit_SSD1306

其实,我的这个代码是基于WS2812FX库中的示例文件做了修改而成。主要修改2点使其能够脱离电脑便携使用:
1.原来wifi配置是固定在代码中,只能在固定的wifi网络使用,修改为wifi配置可以手机端配置账号密码;
2.另外APP需要用到一个IP地址,原代码需要电脑串口打印出来,修改为使用0.96OLED屏幕显示。(手头上有现成的屏幕就拿来用了,如果熟悉Arduino也可以使用手头上其它显示方式,比如LED数码管 )
当然串口上也会打印这个IP地址,你没有连接OLED,只是测试的话也可以用电脑串口工具查看。

同时为了方便升级更新ESP8266,增加了OTA局域网空中升级功能,具体OTA升级方式可搜索相关帖子。

12.jpg


代码配置:
  1. uint8_t  dataPin = D3; // default digital pin used to drive the LED strip
  2. uint16_t numLeds = 13; // default number of LEDs on the strip
复制代码

定义NodeMCU1.0 模块驱动灯串的输出口,D3
定义输出灯串的级联LED数量,这里是13,这个是初始定义,影响不大,在APP中可以动态调整。

  1. Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire);
复制代码

定义0.96OLED屏幕配置,我使用的是128*64分辨率,这里配置成128,64,市面决大多数0.96OLED都是这个规格,如有不同可以按规格配置,比如128,32

  1.   display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Address 0x3C for 128x64
复制代码

定义0.96OLED屏幕I2C地址,0x3C,以实际地址为准,一般不需要更改

  1.   if (!wifiManager.autoConnect("WS2812FX"))
复制代码

引号中的"WS2812FX",为wifi配置页显示配置热点的名称,可以随意修改为你喜欢的名字。注意只能修改引号中的名称,引号格式不能变动。

整个工程代码如下:
  1. /*
  2.   This sketch introduces the idea of patterns.
  3.   A pattern is a collection of segments. Each pattern plays for a specific
  4.   duration and you can setup an array of patterns that play in a continuous loop.
  5.   A web API is included which receives and stores pattern data in JSON format.
  6.   It's the companion sketch for the LEDfx Android app available in the Google Play
  7.   app store: https://play.google.com/store/apps/details?id=com.champlainsystems.ledfx

  8.   Keith Lord - 2018

  9.   LICENSE

  10.   The MIT License (MIT)

  11.   Copyright (c) 2018  Keith Lord

  12.   Permission is hereby granted, free of charge, to any person obtaining a copy
  13.   of this software and associated documentation files (the "Software"), to deal
  14.   in the Software without restriction, including without limitation the rights
  15.   to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
  16.   copies of the Software, and to permit persons to whom the Software is
  17.   furnished to do so, subject to the following conditions:

  18.   The above copyright notice and this permission notice shall be included in
  19.   all copies or substantial portions of the Software.

  20.   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  21.   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  22.   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  23.   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  24.   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  25.   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  26.   THE SOFTWARE.

  27.   CHANGELOG
  28.   2018-02-21 initial version
  29.   2018-11-30 added custom aux functions and OTA update
  30. */

  31. #include <WS2812FX.h>
  32. #include <ESP8266WebServer.h>
  33. #include <WiFiManager.h>
  34. #include <ArduinoOTA.h>
  35. #include <ArduinoJson.h>
  36. #include <EEPROM.h>
  37. #include <Wire.h>
  38. #include <Adafruit_GFX.h>
  39. #include <Adafruit_SSD1306.h>


  40. #define VERSION "2.1.0"

  41. uint8_t  dataPin = D3; // default digital pin used to drive the LED strip
  42. uint16_t numLeds = 13; // default number of LEDs on the strip

  43. Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire);

  44. #define HTTP_PORT 80

  45. #define MAX_NUM_PATTERNS 8
  46. //                       duration, brightness, numSegments, [ { first, last, speed, mode, options, colors[] } ]
  47. //#define DEFAULT_PATTERN {30, 64, 1, { {0, numLeds-1, numLeds*20, FX_MODE_STATIC, NO_OPTIONS, {RED,  BLACK, BLACK}} }}
  48. #define DEFAULT_PATTERN {30, 64, 1, { {0, numLeds-1, numLeds*20, FX_MODE_STATIC, NO_OPTIONS, {RED,  BLUE, BLUE}} }}

  49. typedef struct Pattern { // 208 bytes/pattern
  50.   int duration;
  51.   uint8_t brightness;
  52.   uint8_t numSegments;
  53.   WS2812FX::segment segments[MAX_NUM_SEGMENTS];
  54. } pattern;

  55. // setup a default patterns array
  56. Pattern patterns[MAX_NUM_PATTERNS] = { DEFAULT_PATTERN };
  57. int numPatterns = 1;
  58. int currentPattern = 0;
  59. unsigned long lastTime = 0;

  60. WS2812FX ws2812fx = WS2812FX(numLeds, dataPin, NEO_GRB + NEO_KHZ800);
  61. ESP8266WebServer server(HTTP_PORT);

  62. void (*customAuxFunc[])(void) { // define custom auxiliary functions here
  63.   [] { Serial.println("running customAuxFunc[0]"); },
  64.   [] { Serial.println("running customAuxFunc[1]"); },
  65.   [] { Serial.println("running customAuxFunc[2]"); }
  66. };

  67. void setup() {
  68.   Serial.begin(115200);
  69.   display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Address 0x3C for 128x64
  70.   delay(500);
  71.   display.clearDisplay();
  72.   display.setTextSize(2);
  73.   display.setTextColor(SSD1306_WHITE);
  74.   display.setCursor(0,0);
  75.   display.println("WiFi Setup...");
  76.   display.setCursor(0,0);
  77.   display.display();

  78.   ws2812fx.init();
  79.   ws2812fx.setBrightness(0);
  80.   
  81.   Serial.println("\r\n");

  82.   EEPROM.begin(2048); // for ESP8266 (comment out if using an Arduino)

  83.   // init WiFi
  84.   /*  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  85.     WiFi.mode(WIFI_STA);

  86.     Serial.print("Connecting to " WIFI_SSID);
  87.     while (WiFi.status() != WL_CONNECTED) {
  88.       delay(500);
  89.       Serial.print(".");
  90.     }
  91.     Serial.print("\nServer IP is ");
  92.     Serial.println(WiFi.localIP());
  93.   */
  94.   WiFiManager wifiManager;

  95.   //wifiManager.resetSettings();

  96.   wifiManager.setAPStaticIPConfig(IPAddress(10, 0, 1, 1), IPAddress(10, 0, 1, 1), IPAddress(255, 255, 255, 0));

  97.   wifiManager.setConfigPortalTimeout(180);
  98.   if (!wifiManager.autoConnect("WS2812FX"))
  99.   {
  100.     Serial.println(F("Failed to connect. Reset and try again..."));
  101.     delay(3000);
  102.     //重置并重试
  103.     ESP.reset();
  104.     delay(5000);
  105.   }

  106.   if (WiFi.status() == WL_CONNECTED) {
  107.     Serial.println("WiFi Connected!");
  108.     Serial.print("\nIP ssid: ");
  109.     Serial.println(WiFi.SSID());
  110.     Serial.print("\nServer IP is ");
  111.     Serial.println(WiFi.localIP());

  112.   // text display tests
  113.   display.clearDisplay();
  114.   display.setTextSize(2);
  115.   display.setTextColor(SSD1306_WHITE);
  116.   display.setCursor(0,0);
  117.   display.println(WiFi.localIP());
  118.   display.setCursor(0,0);
  119.   display.display(); // actually display all of the above
  120.   delay(3000);
  121.   display.clearDisplay();
  122.   display.setTextSize(2);
  123.   display.setTextColor(SSD1306_WHITE);
  124.   display.setCursor(0,0);
  125.   display.println("     ");
  126.   display.setCursor(0,0);
  127.   display.display(); // actually display all of the above
  128.   }

  129.   // init OTA
  130.   ArduinoOTA.onStart([]() {
  131.     Serial.println("OTA start");
  132.   });
  133.   ArduinoOTA.onEnd([]() {
  134.     Serial.println("\nOTA end");
  135.   });
  136.   ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
  137.     Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  138.   });
  139.   ArduinoOTA.onError([](ota_error_t error) {
  140.     Serial.printf("Error[%u]: ", error);
  141.     if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
  142.     else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
  143.     else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
  144.     else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
  145.     else if (error == OTA_END_ERROR) Serial.println("End Failed");
  146.   });
  147.   ArduinoOTA.begin();

  148.   // init LED strip with a default segment
  149. //  ws2812fx.init();
  150.   ws2812fx.setBrightness(128);
  151.   ws2812fx.setSegment(0, 0, numLeds - 1, FX_MODE_STATIC, RED, 3000, false);

  152.   // if not rebooting due to catastrophic error, restore pattern data from eeprom
  153.   struct  rst_info  *rstInfo = system_get_rst_info();
  154.   //Serial.print("rstInfo->reason:"); Serial.println(rstInfo->reason);
  155.   if (rstInfo->reason !=  REASON_EXCEPTION_RST) { // not reason 2
  156.     restoreFromEEPROM();
  157.   }

  158.   ws2812fx.start();
  159.   // config and start the web server
  160.   configServer();
  161.   server.begin();
  162. }

  163. void loop() {
  164.   ArduinoOTA.handle();
  165.   ws2812fx.service();
  166.   server.handleClient();

  167.   // if it's time to change pattern, do it now
  168.   unsigned long now = millis();
  169.   if (lastTime == 0 || (now - lastTime > patterns[currentPattern].duration * 1000)) {
  170.     ws2812fx.clear();
  171.     ws2812fx.resetSegments();

  172.     currentPattern = (currentPattern + 1) % numPatterns;
  173.     ws2812fx.setBrightness(patterns[currentPattern].brightness);
  174.     for (int i = 0; i < patterns[currentPattern].numSegments; i++) {
  175.       WS2812FX::segment seg = patterns[currentPattern].segments[i];
  176.       ws2812fx.setSegment(i, seg.start, seg.stop, seg.mode, seg.colors, seg.speed, seg.options);
  177.     }
  178.     lastTime = now;
  179.   }
  180. }

  181. void configServer() {
  182.   server.onNotFound([]() {
  183.     server.sendHeader("Access-Control-Allow-Origin", "*");
  184.     server.send(404, "text/plain", "Page not found");
  185.   });

  186.   // return the WS2812FX status
  187.   // optionally set the running state or run custom auxiliary functions
  188.   server.on("/status", []() {
  189.     server.sendHeader("Access-Control-Allow-Origin", "*");
  190.     String running = server.arg("running");
  191.     if (running.length() > 0) {
  192.       if (running == "true") ws2812fx.start();
  193.       else ws2812fx.stop();
  194.     }

  195.     String auxFunc = server.arg("auxFunc");
  196.     if (auxFunc.length() > 0) {
  197.       int auxFuncIndex = auxFunc.toInt();
  198.       if (auxFuncIndex >= 0 && auxFuncIndex < sizeof(customAuxFunc) / sizeof(customAuxFunc[0])) {
  199.         customAuxFunc[auxFuncIndex]();
  200.       }
  201.     }

  202.     char status[50] = "{"version":"";
  203.     strcat(status, VERSION);
  204.     strcat(status, "","isRunning":");
  205.     strcat(status, ws2812fx.isRunning() ? "true" : "false");
  206.     strcat(status, "}");
  207.     server.send(200, "application/json", status);
  208.   });

  209.   // send the WS2812FX mode info in JSON format
  210.   server.on("/getModes", []() {
  211.     char modes[1000] = "[";
  212.     for (uint8_t i = 0; i < ws2812fx.getModeCount(); i++) {
  213.       strcat(modes, """);
  214.       strcat_P(modes, (PGM_P)ws2812fx.getModeName(i));
  215.       strcat(modes, "",");
  216.     }
  217.     modes[strlen(modes) - 1] = ']';

  218.     server.sendHeader("Access-Control-Allow-Origin", "*");
  219.     server.send(200, "application/json", modes);
  220.   });

  221.   server.on("/upload", HTTP_OPTIONS, []() { // CORS preflight request
  222.     server.sendHeader("Access-Control-Allow-Origin", "*");
  223.     server.sendHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  224.     server.sendHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
  225.     server.send(200, "text/plain", "OK");
  226.   });

  227.   // receive the device info in JSON format and update the pattern data
  228.   server.on("/upload", HTTP_POST, []() {
  229.     String data = server.arg("plain");
  230.     Serial.println(data);

  231.     bool isParseOk = json2patterns(data);

  232.     if (isParseOk && numPatterns > 0) {
  233.       ws2812fx.stop();
  234.       ws2812fx.clear();
  235.       ws2812fx.resetSegments();

  236.       saveToEEPROM();

  237.       currentPattern = 0;
  238.       lastTime = 0;
  239.       ws2812fx.start();
  240.     }

  241.     server.sendHeader("Access-Control-Allow-Origin", "*");
  242.     server.send(200, "application/json", "{"status":200, "message":"OK"}");
  243.   });
  244. }

  245. #define EEPROM_MAGIC_NUMBER 0x010e0d05
  246. void saveToEEPROM() {
  247.   Serial.println("saving to EEPROM");
  248.   EEPROM.put(sizeof(int) * 0, (int)EEPROM_MAGIC_NUMBER);
  249.   EEPROM.put(sizeof(int) * 1, (int)ws2812fx.getPin());
  250.   EEPROM.put(sizeof(int) * 2, (int)ws2812fx.getLength());
  251.   EEPROM.put(sizeof(int) * 3, (int)numPatterns);
  252.   EEPROM.put(sizeof(int) * 4, patterns);
  253.   EEPROM.commit(); // for ESP8266 (comment out if using an Arduino)
  254. }

  255. void restoreFromEEPROM() {
  256.   int magicNumber = 0;
  257.   int pin;
  258.   int length;
  259.   EEPROM.get(sizeof(int) * 0, magicNumber);
  260.   if (magicNumber == EEPROM_MAGIC_NUMBER) {
  261.     Serial.println("restoring from EEPROM");
  262.     EEPROM.get(sizeof(int) * 1, pin);
  263.     if (ws2812fx.getPin() != pin) {
  264.       ws2812fx.setPin(pin);
  265.     }
  266.     EEPROM.get(sizeof(int) * 2, length);
  267.     if (ws2812fx.getLength() != length) {
  268.       ws2812fx.setLength(length);
  269.     }
  270.     EEPROM.get(sizeof(int) * 3, numPatterns);
  271.     EEPROM.get(sizeof(int) * 4, patterns);
  272.   }
  273. }

  274. int modeName2Index(const char* name) {
  275.   for (int i = 0; i < ws2812fx.getModeCount(); i++) {
  276.     if (strcmp_P(name, (PGM_P)ws2812fx.getModeName(i)) == 0) {
  277.       return i;
  278.     }
  279.   }
  280.   return 0;
  281. }

  282. #if ARDUINOJSON_VERSION_MAJOR == 5
  283. //#pragma message("Compiling for ArduinoJson v5")
  284. bool json2patterns(String &json) {
  285.   DynamicJsonBuffer jsonBuffer(2000);
  286.   JsonObject& deviceJson = jsonBuffer.parseObject(json);
  287.   if (deviceJson.success()) {
  288.     ws2812fx.setPin(deviceJson["dataPin"]);
  289.     ws2812fx.setLength(deviceJson["numLeds"]);

  290.     JsonArray& patternsJson = deviceJson["patterns"];
  291.     if (patternsJson.size() > 0 ) {
  292.       numPatterns = 0;
  293.       for (int i = 0; i < patternsJson.size(); i++) {
  294.         JsonObject& patt = patternsJson[i];
  295.         //      bool isEnabled = patt["isEnabled"];
  296.         //      if (! isEnabled) continue; // disabled patterns are not stored

  297.         JsonArray& segmentsJson = patt["segments"];
  298.         if (segmentsJson.size() == 0 ) continue;

  299.         patterns[numPatterns].brightness = patt["brightness"];
  300.         patterns[numPatterns].duration = patt["duration"];

  301.         patterns[numPatterns].numSegments = segmentsJson.size();
  302.         for (int j = 0; j < segmentsJson.size(); j++) {
  303.           JsonObject& seg = segmentsJson[j];
  304.           //seg.printTo(Serial);Serial.println();
  305.           int start = seg["start"];
  306.           if (start < 0 || start >= ws2812fx.getLength()) start = 0;
  307.           patterns[numPatterns].segments[j].start = start;

  308.           int stop = seg["stop"];
  309.           if (stop < 0 || stop >= ws2812fx.getLength()) stop = ws2812fx.getLength() - 1;
  310.           patterns[numPatterns].segments[j].stop = stop;

  311.           if (seg["mode"].is<unsigned int>()) { // seg["mode"] can be a mode number or a mode name
  312.             patterns[numPatterns].segments[j].mode = seg["mode"];
  313.           } else {
  314.             patterns[numPatterns].segments[j].mode = modeName2Index(seg["mode"]);
  315.           }

  316.           int speed = seg["speed"];
  317.           if (speed < SPEED_MIN || speed >= SPEED_MAX) speed = 1000;
  318.           patterns[numPatterns].segments[j].speed = speed;

  319.           patterns[numPatterns].segments[j].options = 0;
  320.           bool reverse = seg["reverse"];
  321.           if (reverse) patterns[numPatterns].segments[j].options |= REVERSE;

  322.           bool gamma = seg["gamma"];
  323.           if (gamma) patterns[numPatterns].segments[j].options |= GAMMA;

  324.           int fadeRate = seg["fadeRate"];
  325.           if (fadeRate > 0) patterns[numPatterns].segments[j].options |= (fadeRate & 0x7) << 4;

  326.           int size = seg["size"];
  327.           if (size > 0) patterns[numPatterns].segments[j].options |= (size & 0x3) << 1;

  328.           JsonArray& colors = seg["colors"]; // the web interface sends three color values
  329.           // convert colors from strings ('#ffffff') to uint32_t
  330.           patterns[numPatterns].segments[j].colors[0] = strtoul(colors[0].as<char*>() + 1, 0, 16);
  331.           patterns[numPatterns].segments[j].colors[1] = strtoul(colors[1].as<char*>() + 1, 0, 16);
  332.           patterns[numPatterns].segments[j].colors[2] = strtoul(colors[2].as<char*>() + 1, 0, 16);
  333.         }
  334.         numPatterns++;
  335.         if (numPatterns >= MAX_NUM_PATTERNS) break;
  336.       }
  337.     } else {
  338.       Serial.println(F("JSON contains no pattern data"));
  339.       return false;
  340.     }
  341.   } else {
  342.     Serial.println(F("Could not parse JSON payload"));
  343.     return false;
  344.   }
  345.   return true;
  346. }
  347. #endif

  348. #if ARDUINOJSON_VERSION_MAJOR == 6
  349. //#pragma message("Compiling for ArduinoJson v6")
  350. bool json2patterns(String &json) {
  351.   // in ArduinoJson v6 a DynamicJsonDocument does not expand as needed
  352.   // like it did in ArduinoJson v5. So rather then try to compute the
  353.   // optimum heap size, we'll just allocated a bunch of space on the
  354.   // heap and hope it's enough.
  355.   int freeHeap = ESP.getFreeHeap();
  356.   DynamicJsonDocument doc(freeHeap - 3096); // allocate all of the available heap except 3kB
  357.   DeserializationError error = deserializeJson(doc, json);
  358.   if (!error) {
  359.     JsonObject deviceJson = doc.as<JsonObject>();
  360.     ws2812fx.setPin(deviceJson["dataPin"]);
  361.     ws2812fx.setLength(deviceJson["numLeds"]);

  362.     JsonArray patternsJson = deviceJson["patterns"];
  363.     if (patternsJson.size() > 0 ) {
  364.       numPatterns = 0;
  365.       for (int i = 0; i < patternsJson.size(); i++) {
  366.         JsonObject patt = patternsJson[i];
  367.         //      bool isEnabled = patt["isEnabled"];
  368.         //      if (! isEnabled) continue; // disabled patterns are not stored

  369.         JsonArray segmentsJson = patt["segments"];
  370.         if (segmentsJson.size() == 0 ) continue;

  371.         patterns[numPatterns].brightness = patt["brightness"];
  372.         patterns[numPatterns].duration = patt["duration"];

  373.         patterns[numPatterns].numSegments = segmentsJson.size();
  374.         for (int j = 0; j < segmentsJson.size(); j++) {
  375.           JsonObject seg = segmentsJson[j];
  376.           //seg.printTo(Serial);Serial.println();

  377.           int start = seg["start"];
  378.           if (start < 0 || start >= ws2812fx.getLength()) start = 0;
  379.           patterns[numPatterns].segments[j].start = start;

  380.           int stop = seg["stop"];
  381.           if (stop < 0 || stop >= ws2812fx.getLength()) stop = ws2812fx.getLength() - 1;
  382.           patterns[numPatterns].segments[j].stop = stop;

  383.           if (seg["mode"].is<unsigned int>()) { // seg["mode"] can be a mode number or a mode name
  384.             patterns[numPatterns].segments[j].mode = seg["mode"];
  385.           } else {
  386.             patterns[numPatterns].segments[j].mode = modeName2Index(seg["mode"]);
  387.           }

  388.           int speed = seg["speed"];
  389.           if (speed < SPEED_MIN || speed >= SPEED_MAX) speed = 1000;
  390.           patterns[numPatterns].segments[j].speed = speed;

  391.           patterns[numPatterns].segments[j].options = 0;
  392.           bool reverse = seg["reverse"];
  393.           if (reverse) patterns[numPatterns].segments[j].options |= REVERSE;

  394.           bool gamma = seg["gamma"];
  395.           if (gamma) patterns[numPatterns].segments[j].options |= GAMMA;

  396.           int fadeRate = seg["fadeRate"];
  397.           if (fadeRate > 0) patterns[numPatterns].segments[j].options |= (fadeRate & 0x7) << 4;

  398.           int size = seg["size"];
  399.           if (size > 0) patterns[numPatterns].segments[j].options |= (size & 0x3) << 1;

  400.           JsonArray colors = seg["colors"]; // the web interface sends three color values
  401.           // convert colors from strings ('#ffffff') to uint32_t
  402.           patterns[numPatterns].segments[j].colors[0] = strtoul(colors[0].as<char*>() + 1, 0, 16);
  403.           patterns[numPatterns].segments[j].colors[1] = strtoul(colors[1].as<char*>() + 1, 0, 16);
  404.           patterns[numPatterns].segments[j].colors[2] = strtoul(colors[2].as<char*>() + 1, 0, 16);
  405.         }
  406.         numPatterns++;
  407.         if (numPatterns >= MAX_NUM_PATTERNS)  break;
  408.       }
  409.     } else {
  410.       Serial.println(F("JSON contains no pattern data"));
  411.       return false;
  412.     }
  413.   } else {
  414.     Serial.print(F("Could not parse JSON payload: "));
  415.     Serial.println(error.c_str());
  416.     return false;
  417.   }
  418.   return true;
  419. }
  420. #endif
复制代码


代码打包:
ws2812fx_patterns_web.zip (5.16 KB, 下载次数: 47)

库文件打包:
Library.zip (1.63 MB, 下载次数: 88)

解压到C:\Users\...(对应你的账户)\Documents\Arduino\libraries

注意:Adafruit_SSD1306 库需要进库文件目录修改OLED配置为128*64
附件提供的库已配置好。
QQ截图20191220120908.jpg


2.  配套安卓APP

安卓APP:
LEDfx_v2.1.0_apkpure.com.zip (4.36 MB, 下载次数: 175)



三.具体使用方法:

编辑配置OK之后,连接NodeMCU1.0目标板,点击《上传》代码上传到目标板中
图片.png

手机端下载安装好APP,连接好电路,插入Micro USB线,开发板上电。
OLED屏显示:WiFi Setup...
QQ图片20191220112114.jpg

系统进入wifi配置状态,打开手机wifi设置搜索热点:WS2812FX, 并点击登录到该热点
QQ图片20191220112234.jpg QQ图片20191220112546.jpg

点击第一个配置WiFi,找到你路由器的ID点击,然后输入密码,最后点击save保存,模块会自动配置wifi,配置界面会自动退出。正确配置并连接网络就不会再出现该热点,没有连接到网络的话可以再次连接热点配置,更换网络同理。
QQ图片20191220112921.jpg


同时注意,OLED显示屏会提示一个IP地址。(因为OLED的特性,为了保护OLED屏幕,该地址只显示5秒左右,可在程序中更改时间或长显)
记住该地址,后面的APP中需要该地址进行操作。

QQ图片20191220113540.jpg

手机连接到开发板同一个WiFi,打开帖子中提供的LEDfx APP,点击Devices后面的+

QQ图片20191220114209.jpg

几个关键的设置:

IP or Host: 输入之前设置wifi后OLED屏显示的IP地址,我这里是:192.168.3.3,你的需要按实际填写。
Data Pin:你的驱动脚,帖子中是连接的D3, gpio脚是:0
Num LEDs:你的灯串的数量,我的硬件是:60
填好后,点击Pattems后面+号
1.jpg

这里可以设置灯串的亮度 Brightness,时间 Duration 保持默认或按需修改。
点击Segments后面的+号
2.jpg

设置一些参数,
灯串的起始,60灯串起始为0-59.
Mode下拉选择闪烁模式,几十种各式各样的闪烁可选。
速度,颜色等等,设置后点击播放图标可即时看到效果。
4.jpg
3.jpg

退回到最上层菜单,点击播放按钮,即可按你的配置闪烁,点击SAVE可以保存到ESP8266的EEPROM中,下次上电直接可以调取保存的模式闪烁。
QQ图片20191220114549.jpg

APP可以多路,多组合闪烁,组合绚丽多彩的闪灯模式。如果使用矩阵式灯串,甚至可以设置显示出字体。
作为家里的氛围灯小装饰,孩子的创意玩具,甚至情侣的表白神器,就靠各位发挥自己的想象力了。
QQ图片20191220111915.jpg






ESP8266系列制作:

ESP8266 驱动圆形60串WS2812全彩LED制作的模拟走时动画时钟(DIY系列二)
https://www.mydigit.cn/forum.php?mod=viewthread&tid=109677

ESP8266 WIFI自动校时LED点阵滚动时钟(DIY系列三)
https://www.mydigit.cn/forum.php?mod=viewthread&tid=111420

ESP8266基于Arduino使用FastLED驱动WS2812B灯带的心得
https://www.mydigit.cn/forum.php?mod=viewthread&tid=112534


打赏

参与人数 3家元 +60 收起 理由
lemontreenm + 20 非常好的DIY,文件能够百度网盘共享就更好.
wjxhone + 20 熱心助人
小霞 + 20 謝謝分享

查看全部打赏

发表于 2019-12-22 09:34:47 | 显示全部楼层
抢个沙发,囤了不少8266模块,可以研究玩了。
回复 支持 反对

使用道具 举报

发表于 2019-12-22 13:20:06 | 显示全部楼层
前段时间我也在捣鼓这个项目,我是直接用WS2812FX库中的示例文件,但编译时struct  rst_info  *rstInfo = system_get_rst_info();这里一直通不过,后来将这几句删掉后通过了。现在问题是,模块能连上路由器,灯带也显示默认的全部红色点亮,但在手机端操作LEDfx软件无效。不知楼主能否提供一个编译好的BIN固件,谢谢了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-12-23 09:41:57 | 显示全部楼层
wjxhone 发表于 2019-12-22 13:20
前段时间我也在捣鼓这个项目,我是直接用WS2812FX库中的示例文件,但编译时struct  rst_info  *rstInfo = s ...

下载我的库和程序试过没?你要的BIN
链接: https://pan.baidu.com/s/1lMHHKACcl0ATKzfAdTP3Yg 提取码: phdn

ws2812fx_patterns_web.ino.zip

264.44 KB, 下载次数: 12, 下载积分: 家元 -55

回复 支持 反对

使用道具 举报

发表于 2019-12-23 19:07:29 | 显示全部楼层
zengcym 发表于 2019-12-23 09:41
下载我的库和程序试过没?你要的BIN
链接: https://pan.baidu.com/s/1lMHHKACcl0ATKzfAdTP3Yg 提取码: p ...

感谢提供固件,刚开始直接烧录固件还是不行,因这个模块写过其它程序,所以将模块程序擦除干净再重新烧录就正常了,这个控制器比网上另一个开源的项目ESPixelStick花样还要多。
回复 支持 1 反对 0

使用道具 举报

发表于 2019-12-25 12:07:59 | 显示全部楼层
太厉害了,先收藏,再学习!
回复 支持 反对

使用道具 举报

发表于 2019-12-25 22:47:40 | 显示全部楼层
嗯。有点意思 ,可以手机控制
回复 支持 反对

使用道具 举报

发表于 2019-12-27 01:35:58 来自手机浏览器 | 显示全部楼层
8266-01可以用吧?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-12-27 09:04:17 | 显示全部楼层

ESP-01-ESP8266-pinout-gpio-pin-1.jpg

DIN接GPIO0, I2C没有拉出来,OLED就不接了,用电脑串口工具查看IP就行了。



点评

可以用的,要自己写I2C协议,指定IO口就行  详情 回复 发表于 2020-1-6 18:12
回复 支持 反对

使用道具 举报

发表于 2020-1-6 18:12:25 | 显示全部楼层
zengcym 发表于 2019-12-27 09:04
DIN接GPIO0, I2C没有拉出来,OLED就不接了,用电脑串口工具查看IP就行了。

可以用的,要自己写I2C协议,指定IO口就行
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-1-7 11:18:03 | 显示全部楼层
水表君 发表于 2020-1-6 18:12
可以用的,要自己写I2C协议,指定IO口就行

可以的,当然对于新手就稍复杂些。
回复 支持 反对

使用道具 举报

发表于 2020-1-8 12:19:53 | 显示全部楼层
谢谢分享 ,学习了
回复 支持 反对

使用道具 举报

发表于 2020-1-10 16:53:25 来自手机浏览器 | 显示全部楼层
我新的微星主板支持rgb LED现货,还是12V的,可以玩玩
回复 支持 反对

使用道具 举报

发表于 2020-1-13 15:57:51 | 显示全部楼层
空闲时间用KiCad画了张板子,用的是不到6元的那种模块,能正常使用。链接:https://pan.baidu.com/s/1XXW4rZNfz_B4fPolACO_HQ 提取码:noxx ESP8266_WS2812.jpg

回复 支持 1 反对 0

使用道具 举报

发表于 2020-1-15 23:01:25 | 显示全部楼层
这个帖子解决了我现在想做的
回复 支持 反对

使用道具 举报

发表于 2020-1-19 01:54:49 来自手机浏览器 | 显示全部楼层
不一定要OLED 屏幕,普通LCD就行了
回复 支持 反对

使用道具 举报

发表于 2020-1-23 22:46:14 | 显示全部楼层
这个怎么回事啊 不懂啊 大神们

编译出现了这个提示

编译出现了这个提示
回复 支持 反对

使用道具 举报

发表于 2020-2-11 19:27:26 来自手机浏览器 | 显示全部楼层
厉害,玩不转。。。。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2024-3-29 03:26 , Processed in 0.171600 second(s), 14 queries , Redis On.

Powered by Discuz!

© 2006-2023 smzj.net

快速回复 返回顶部 返回列表