ESP8266: Controlling WS2812B RGB LEDs (NeoPixels)

ESP8266: Controlling WS2812B RGB LEDs (NeoPixels)

by ACROBOTIC Industries

Overview

Time to complete: 10–15min; Level of difficulty: Beginner

In this activity we’ll use an ESP8266 ESP-12E Development Board to individually control the color and brightness of an array of WS2812B RGB LEDs.

List of Materials

  • 1 x ESP8266 (ESP-12E) Development Board
  • 1 x Hum. and Temp. Sensor (DHT-22)
  • 1 x Jumper Wires Male/Male - 10-pack
  • 1 x Half-Sized Solderless Breadboard

Obtaining the Code

For this activity, we'll be using the wonderful WS2812/NeoPixels Arduino Library developed by Github user Makuna.  From the many alternatives, this is the one that works the best for our ESP8266 Development Board; the library's source code can be found at:

We'll use a simple process to install this library and make it available for use in our example programs below.


Installing the Library

There are a few different ways to install a library in the Arduino IDE.  For detailed, step-by-step instructions on all the different ways to do so please visit our tutorial.  The simplest method is to use the built-in library manager in the Arduino IDE. It can be accessed via the menu option Sketch → Include Library → Manage Libraries…

In the search field we can enter "Makuna NeoPixel" and install the resulting option:

Install NeoPixelBus WS2812 Library in Arduino IDE

Before diving into the code, let's review a bit the operation of WS2812 RGB LEDs, and how their brightness and color are controlled.


How WS8212 RGB LEDs Work

For a detailed explanation about how WS2812 RGB LEDs (aka NeoPixels) work, please visit our neat tutorial!


Wiring: NeoPixelTest

One of the most attractive features of the WS2812 is that all we need to control them is a very simple circuit, which provides Power, Ground, and 1 Control Input in order to create colorful lighting displays.  The Control Input is used to send the color data to each LED that needs to be controlled.  

As seen in the previous step, the data is transmitted by changing the HIGH and LOW state of the Control Input pin using very a precise (and fast!) timing schedule.  There are different methods for changing between these HIGH and LOW states, and the one that works best on the ESP8266 is by using its built-in (hardware) functionality I2S.  For more details you can read over the library's wiki: https://github.com/Makuna/NeoPixelBus/wiki/NeoPixelBus-object

In order to use I2S to send data to the LEDs, we have to use the RX pin on our ESP8266 development board.  If you have a different board or ESP8266 module, you might need to disconnect this data line in order to re-program the chip.  Luckily, this is not necessary using our board:

Wiring Diagram For WS2812 Strip And ESP8266 Development Board

A trick to easily wire these LED strips to the ESP8266 Development Board is to use Male/Male jumper wires with one end plugged into each of the pins of the female (Data In) JST connector of the strip, and the other end plugged into the solderless breadboard!

Once we've connected the strip as shown in the diagram, we're ready to upload code to the board that will allow us to control the color and brightness of each NeoPixel WS2812B RGB LED!


Code Walkthrough: NeoPixelTest

To ensure that everything's working correctly, we use the example code NeoPixelTest that comes with the NeoPixelBus library.  As usal, we can find this example via the menu option File → Examples → NeoPixelBus by Makuna → NeoPixelTest

NeoPixelBus Library Example

Once we've open the example code, we typically would change the of const uint16_t PixelCount = 4; to match the number of 'NeoPixels' (LEDs) that we have connected (60 in our case).  However, this test code only uses 4 LEDs so it's okay to use the default value.

 

#include 

const uint16_t PixelCount = 4; // this example assumes 4 pixels, making it smaller will cause a failure
const uint8_t PixelPin = 2;  // make sure to set this to the correct pin, ignored for Esp8266

#define colorSaturation 128

// three element pixels, in different order and speeds
NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);

The first section of the code includes the NeoPixelBus library so that it's available in the code.  Also, it includes the definitions of different constant values that we'll be using later.  The colorSaturation is the desired brightness values for the LEDs, it can range from 0 (fully off) to 255 (fully on) and it's set by default to 50%.

Note that depending on your specific NeoPixel strip manufacturer, the order of the individual LEDs inside each 'pixel' might be different.  If the colors do not match you can simply change the last line above to:

NeoPixelBus<NeoRgbFeature, Neo400KbpsMethod> strip(PixelCount, PixelPin);

The next section of the code defines the different colors that will be used later in the code.  It uses a specific data structure as defined in the NeoPixelBus library:

RgbColor red(colorSaturation, 0, 0);
RgbColor green(0, colorSaturation, 0);
RgbColor blue(0, 0, colorSaturation);
RgbColor white(colorSaturation);
RgbColor black(0);

HslColor hslRed(red);
HslColor hslGreen(green);
HslColor hslBlue(blue);
HslColor hslWhite(white);
HslColor hslBlack(black);

After all the constants definitions, we find the setup function where a few debug messages are printed (using the Serial object as it's typical), and the communication to the NeoPixels is started.  The Begin() method initializes the communication as well as sets in memory the brightness values to 0 for all the NeoPixels (as many as we have defined in PixelCount).  The Show() method then sends the data from memory to the physical LEDs.

    // this resets all the neopixels to an off state
    strip.Begin();
    strip.Show();

Finally, the loop() function sends the color data to the first four LEDs.  Note that this process consists of first setting the different colors in memory using the SetPixelColor() method, and then it sends it to the physical NeoPixels over the data line using the aforementioned Show() method.

    strip.SetPixelColor(0, red);
    strip.SetPixelColor(1, green);
    strip.SetPixelColor(2, blue);
    strip.SetPixelColor(3, white);
    strip.Show();

The code makes the NeoPixels hold their color for 5 seconds, then turns them off by setting the color to black, and then turns them on to the colors defined using the HslColor definitions!  As a result we see the first four NeoPixels blink on and off to their respective colors (red, green, blue, and white).


Making Custom Animations

As we can see, it's straightforward to set the colors of all the NeoPixels connected to our microcontroller/SoC, and display them using the SetPixelColor() and Show() functions of the NeoPixelBus library.  We now can use our imagination to come up with ideas for creating custom animations we'd like to see display.

ESP8266 Development Board with WS2812B Strip

The rest of the examples in the NeoPixelBus library are a good start to see different methods to create animations (e.g., NeoPixelCylon), but we also wanted to show you how to create a "rainbow" pattern where each LED has a different color covering the entire color space.  We've posted this code example in our Github repository:

You can copy and paste the code from the .ino file located inside the Rainbow folder.


Code Walkthrough: Rainbow

The initial constants and setup() function are similar to the NeoPixelTest example.  The code that generates the animation is split between the loop() function and a custom (user-defined) function called Wheel(). The Wheel() function takes in a value from 0 to 255 and returns a color comprised by red, gren, and blue values.  The way the color is chosen is by splitting the color space in 3 segments:

  • decreasing red (255 to 0), no green (0), increasing blue (0 to 255)
  • no red (0), increasing green (0 to 255), decreasing blue (255 to 0)
  • increasing red (0 to 255), decreasing green (255 to 0), no blue (0)

Notice that in each of the three segments, one color is set to off, one color goes from full brightness to zero, whereas the third goes from zero to full brightness.  The two colors that are on are mixed as their brightness increase/decrease.

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
RgbColor Wheel(uint8_t WheelPos) 
{
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) 
  {
    return RgbColor(255 - WheelPos * 3, 0, WheelPos * 3);
  } else if(WheelPos < 170) 
  {
    WheelPos -= 85;
    return RgbColor(0, WheelPos * 3, 255 - WheelPos * 3);
  } else 
  {
    WheelPos -= 170;
    return RgbColor(WheelPos * 3, 255 - WheelPos * 3, 0);
  }
}

If we follow each segment in the order listed above waiting for the values to transition fully, we see the following behavior: in the first segment we go from full red to full blue (across the red-blue colorspace), then in the second we go from full blue to full green (across the blue-green colorspace), and in the last we go from full green back to full red (across the green-red colorspace).  This provides a convenient way to navigate the entire colorspace.

So the only problem is, how do we assign each NeoPixel a different value?

Because of the way the Wheel() function works, we have to use the numbers between 0 and 255 to choose all the different colors.  The simplest case would be having 256 NeoPixels, in which case we'd assign the color value corresponding to 0 to the 1st NeoPixel (red as this is the color returned by Wheel(0)), and the color value corresponding to 255 to the last NeoPixel (also red as this is the color returned by Wheel(255)).

Once we'd assign all the different color values and displayed them using the Show() function, we could add 1 to all of them so we'd see the colors "traveling" along the length of the strip!  Of course, we'd need to ensure that the last NeoPixel whose value would be 256 (255+1) rolls back to 0 so that the Wheel() function is able to return the correct value.

What we've explained above can be implemented using two for loops, an outer loop for "shifting" the values down the length of the strip, and the inner loop for assigning the different colors to each NeoPixel on the strip.  With a little arithmetic trickery, we can also account for cases where the number of NeoPixels are not 256.

  for(uint16_t j=0; j<256*5; j++) // complete 5 cycles around the color wheel
  { 
    for(uint16_t i=0; i<PixelCount; i++)
    {
      // generate a value between 0~255 according to the position of the pixel
      // along the strip
      pos = ((i*256/PixelCount)+j) & 0xFF;
      // calculate the color for the ith pixel
      color = Wheel( pos );
      // set the color of the ith pixel
      strip.SetPixelColor(i, color);
    }
    strip.Show();
    delay(50);
  } 

Leave a comment

  • Please note, comments must be approved before they are published

$1 Days
$2 Hours
$3 Minutes
$4 Second
{ "en":{ "general": { "field": { "required": "Required", "actions": "Actions", "top_btn": "Top" }, "accessibility": { "skip_to_content": "Skip to content", "close_modal": "Close (esc)" }, "meta": { "tags": "Tagged \"[[ tags ]]\"", "page": "Page [[ page ]]" }, "404": { "title": "404 Page Not Found", "subtext": "The page you requested does not exist.", "link": "Continue shopping" }, "pagination": { "previous": "Previous", "next": "Next", "current_page": "Page [[ current ]] of [[ total ]]" }, "password_page": { "opening_soon": "Opening Soon", "login_form_heading": "Enter store using password", "login_form_password_label": "Password", "login_form_password_placeholder": "Your password", "login_form_submit": "Enter", "signup_form_email_label": "Email", "signup_form_success": "We will send you an email right before we open!", "admin_link_html": "Are you the store owner? Log in here<\/a>", "password_link": "Enter using password", "powered_by_shopify_html": "This shop will be powered by [[ shopify ]]" }, "social": { "share_on_facebook": "Share", "share_on_twitter": "Tweet", "share_on_pinterest": "Pin it", "alt_text": { "share_on_facebook": "Share on Facebook", "share_on_twitter": "Tweet on Twitter", "share_on_pinterest": "Pin on Pinterest" } }, "search": { "no_results_html": "Your search for \"[[ terms ]]\" did not yield any results.", "results_with_count": { "one": "[[ count ]] result for \"[[ terms ]]\"", "other": "[[ count ]] results for \"[[ terms ]]\"" }, "title": "Search our site", "placeholder": "Search", "submit": "Submit", "close": "Close search" }, "newsletter_form": { "newsletter_email": "Join our mailing list", "email_placeholder": "Email address", "confirmation": "Thanks for subscribing", "submit": "Subscribe", "show_me_text": "Do not show me again" }, "filters": { "show_more": "Show More", "show_less": "Show Less" }, "breadcrumbs": { "home": "Home", "create_account": "Create account", "account": "Account", "addresses": "Addresses" }, "item": { "remove": "Remove Item" } }, "sections": { "header": { "top_header_login": "Login", "top_header_register": "Register", "top_header_wishlist": "Wish list", "register_dropdown": "No account? Create one here", "forgot": "Forgot password", "all_collection": "All Collections", "world_wide_delivery": "Worldwide delivery", "shipping_text": "Free UK Delivery on orders over £ 100", "hot_line": "Hot line" }, "menu": { "mobile_menu_tab": "Menu", "mobile_account_tab": "Account", "mobile_settings_tab": "Settings" }, "slideshow": { "next_slide": "Next slide", "previous_slide": "Previous slide", "pause_slideshow": "Pause slideshow", "play_slideshow": "Play slideshow", "play_video": "Play video", "close_video": "Close video" }, "map": { "get_directions": "Get directions", "address_error": "Error looking up that address", "address_no_results": "No results for that address", "address_query_limit_html": "You have exceeded the Google API usage limit. Consider upgrading to a Premium Plan<\/a>.", "auth_error_html": "There was a problem authenticating your Google Maps account. Create and enable the JavaScript API<\/a> and Geocoding API<\/a> permissions of your app." } }, "blogs": { "article": { "view_all": "View all", "all_topics": "All topics", "by_author": "by [[ author ]]", "posted_in": "Posted in", "read_more": "Read more", "back_to_blog": "Back to [[ title ]]" }, "comments": { "title": "Leave a comment", "name": "Name", "email": "Email", "message": "Message", "post": "Post comment", "moderated": "Please note, comments must be approved before they are published", "success_moderated": "Your comment was posted successfully. We will publish it in a little while, as our blog is moderated.", "success": "Your comment was posted successfully! Thank you!", "comments_with_count": { "one": "[[ count ]] comment", "other": "[[ count ]] comments" } } }, "cart": { "general": { "title": "Your cart", "note": "Add a note to your order", "remove": "Remove", "subtotal": "Subtotal", "savings": "You're saving", "shipping_at_checkout": "Shipping & taxes calculated at checkout", "update": "Update", "checkout": "Process Check out", "empty": "Your cart is currently empty.", "cookies_required": "Enable cookies to use the shopping cart", "edit": "Edit", "cancel": "Cancel", "continue_shopping": "Continue shopping", "recently_added_item": "Recently added item(s)", "remove_item": "Remove This Item", "view_and_edit_cart": "View and edit cart", "clear": "Clear cart", "empty_page_title": "Shopping Cart is Empty", "here": "here", "empty_continue_html": "Click here to continue shopping.", "processing": "Processing...", "items_count_label" : "[[ count ]] item(s) in your cart", "ok" : "Ok" }, "label": { "product": "Product", "price": "Price", "quantity": "Quantity", "total": "Total", "total_item": "Total item", "sub_total_top": "Cart Subtotal" } }, "collections": { "general": { "view_all": "View all", "clear_all": "Clear All", "no_matches": "Sorry, there are no products in this collection", "items_with_count": { "one": "[[ count ]] product", "other": "[[ count ]] products" }, "load_more": "Load More", "sidebar_btn": "Filter by" }, "sorting": { "title": "Sort by", "manual": "Featured", "best_selling": "Best Selling", "title_ascending": "Alphabetically, A-Z", "title_descending": "Alphabetically, Z-A", "price_ascending": "Price, low to high", "price_descending": "Price, high to low", "created_descending": "Date, new to old", "created_ascending": "Date, old to new" }, "filters": { "title_tags": "Filter", "all_tags": "All products", "categories": "categories", "title": "Filter", "color": "Color", "size": "Size", "brand": "Brand", "price": "Price", "green": "Green", "blue": "Blue", "red": "Red", "pink": "Pink", "black": "Black", "purple": "Purple", "white": "White", "orange": "Orange" }, "product_item": { "quick_shop": "Quick View", "compare": "Compare", "wishlist": "Add to Wishlist" } }, "contact": { "form": { "name": "Name", "email": "Email", "phone": "Phone Number", "message": "Message", "submit": "Submit", "post_success": "Thanks for contacting us. We'll get back to you as soon as possible.", "address": "Address", "telephone": "Telephone", "title": "Write us", "required": "Required" } }, "customer": { "account": { "title": "My Account", "details": "Account Details", "view_addresses": "View Addresses", "return": "Return to Account Details" }, "activate_account": { "title": "Activate Account", "subtext": "Create your password to activate your account.", "password": "Password", "password_confirm": "Confirm Password", "submit": "Activate Account", "cancel": "Decline Invitation" }, "addresses": { "title": "Your Addresses", "default": "Default", "add_new": "Add a New Address", "edit_address": "Edit address", "first_name": "First Name", "last_name": "Last Name", "company": "Company", "address1": "Address1", "address2": "Address2", "city": "City", "country": "Country", "province": "Province", "zip": "Postal\/Zip Code", "phone": "Phone", "set_default": "Set as default address", "add": "Add Address", "update": "Update Address", "cancel": "Cancel", "edit": "Edit", "delete": "Delete", "delete_confirm": "Are you sure you wish to delete this address?" }, "login": { "title": "Login", "desc": "If you have an account, sign in with your email address.", "email": "Email", "password": "Password", "forgot_password": "Forgot your password?", "sign_in": "Sign In", "guest_title": "Continue as a guest", "guest_continue": "Continue" }, "orders": { "title": "Order History", "order_number": "Order", "date": "Date", "payment_status": "Payment Status", "fulfillment_status": "Fulfillment Status", "total": "Total", "none": "You haven't placed any orders yet." }, "order": { "title": "Order [[ name ]]", "date": "Placed on [[ date ]]", "cancelled": "Order Cancelled on [[ date ]]", "cancelled_reason": "Reason: [[ reason ]]", "billing_address": "Billing Address", "payment_status": "Payment Status", "shipping_address": "Shipping Address", "fulfillment_status": "Fulfillment Status", "discount": "Discount", "shipping": "Shipping", "tax": "Tax", "product": "Product", "sku": "SKU", "price": "Price", "quantity": "Quantity", "total": "Total", "fulfilled_at": "Fulfilled [[ date ]]", "subtotal": "Subtotal" }, "recover_password": { "title": "Reset your password", "email": "Email", "submit": "Submit", "cancel": "Cancel", "subtext": "We will send you an email to reset your password.", "success": "We've sent you an email with a link to update your password." }, "reset_password": { "title": "Reset account password", "subtext": "Enter a new password for [[ email ]]", "password": "Password", "password_confirm": "Confirm Password", "submit": "Reset Password" }, "register": { "title": "Create Account", "first_name": "First Name", "last_name": "Last Name", "email": "Email", "password": "Password", "submit": "Create", "desc": "Creating an account is easy. Just fill in the form below." } }, "homepage": { "onboarding": { "product_title": "Your product's name", "product_description": "This area is used to describe your product’s details. Tell customers about the look, feel, and style of your product. Add details on color, materials used, sizing, and where it was made.", "collection_title": "Your collection's name", "blog_title": "Your post's title", "blog_excerpt": "Your store hasn’t published any blog posts yet. A blog can be used to talk about new product launches, tips, or other news you want to share with your customers. You can check out Shopify’s ecommerce blog for inspiration and advice for your own store and blog.", "blog_author": "Author name", "no_content": "This section doesn’t currently include any content. Add content to this section using the sidebar." } }, "layout": { "navigation": { "search": "Search", "toggle": "expand\/collapse", "expand": "expand", "collapse": "collapse", "all_categories": "All Categories" }, "cart": { "title": "Cart", "items_count": { "one": "item", "other": "items" } }, "customer": { "account": "Account", "log_out": "Log out", "logout": "Log out", "log_in": "Log in", "create_account": "Create account", "sign_up": "Sign up", "wishlist": "Wishlist" }, "footer": { "social_platform": "[[ name ]] on [[ platform ]]" }, "list_page": { "grid": "Grid", "list": "List" } }, "products": { "product": { "regular_price": "Regular price", "sold_out": "Sold out", "unavailable": "Unavailable", "on_sale": "Sale", "quantity": "Quantity", "add_to_cart": "Add to cart", "back_to_collection": "Back to [[ title ]]", "related_title": "Related Products", "qty_increase": "Increase", "qty_decrease": "Decrease", "deal_days": "Days", "deal_hours": "Hours", "deal_minutes": "Minutes", "deal_second": "Second", "select_option": "Select Option", "add_to_wishlist": "Add to Wishlist", "add_to_review": "Add to review", "compare_success_msg": "[[ product_title ]] has added to comparing box successful", "compare_exist_msg": "[[ product_title ]] is exist in comparing box", "compare_cart_msg": "[[ product_title ]] has added to shopping cart", "compare_remove_msg": "[[ product_title ]] has removed from comparing box", "compare_remove_msg": "[[ product_title ]] has removed from comparing box", "comparing_box": "Comparing box", "compare_no_items": "There is no items in comparing box", "wishlist_success_msg": "[[ product_title ]] has added to wishlist successful", "wishlist_exist_msg": "[[ product_title ]] is exist in wishlist", "wishlist_cart_msg": "[[ product_title ]] has added to shopping cart", "wishlist_box": "Wishlist", "wishlist_remove_msg": "[[ product_title ]] has removed from wishlist", "wislist_no_items": "There is no items in wishlist", "upsell_cart_msg": "\"[[ product_title ]]\" has added to shopping cart", "upsell_block_title": "Frequently bought with \"[[ product_title ]]\"", "upsell_cart_qty": "[[ count ]] item(s)", "upsell_product_page_title": "You may also like these products", "upsell_checkout_btn": "Checkout", "share": "Share product", "share_on_facebook": "Share on Facebook", "share_on_twitter": "Share on Twitter", "share_on_pinterest": "Share on Pinterest", "share_on_google": "Share on Google+", "share_on_linkedin": "Share on LinkedIn", "availability": "Availability", "in_stock": "In Stock", "out_of_stock": "Out of stock", "quick_overview": "Quick Overview", "details": "Details", "reviews": "Reviews", "first_review": "Be the first review", "tags": "Product Tags", "size_chart": "Size Chart", "options": "Options", "vendor": "Vendor", "features": "Features", "sale_left_text": "[[ sales ]] SOLD. HURRY! ONLY A FEW LEFT!", "checkout_text": "Secured and trusted checkout with" }, "upsell": { "recommend_text": "Someone purchased a", "minute_ago": "minutes ago" } }, "gift_cards": { "issued": { "title_html": "Here's your [[ value ]] gift card for [[ shop ]]!", "subtext": "Your gift card", "disabled": "Disabled", "expired": "Expired on [[ expiry ]]", "active": "Expires on [[ expiry ]]", "redeem_html": "Use this code at checkout to redeem your [[ value ]] gift card", "shop_link": "Start shopping", "print": "Print this gift card", "remaining_html": "[[ balance ]] left", "add_to_apple_wallet": "Add to Apple Wallet" } }, "date_formats": { "month_day_year": "%B %d, %Y" } } }