How to Make an Internet LED Strip with ESP8266, Python, and WordPress

internet led strip
Behind the scenes

You may have already used my internet LED strip here on Maker WS’s cam page and today I am going to teach you how to make one of your very own!

If you haven’t made one already, you will need to have already made a WiFi controllable LED strip similar to the one from my guide here. To control it remotely via this website, you’ll also need a computer connected to the internet on the same local network as the LED strip. For this guide, that computer will need to be running on Linux and have Python 3 installed. I recommend using a Raspberry Pi Zero.

But to really make it your own, you’ll also need a WordPress site with a public IP or domain name. If that’s not part of your life plan right now, though, you are always welcome to use Maker WS to control your strip.

makerws interactive web cam internet LED strip
makerws.com/cam or click the link in the top nav bar

Table of Contents


Internet LED strip firmware

We’re going to adapt a sketch from circuits4you to make a simple form that can receive the RGB values. I’m going to highlight a few blocks of code. You can find the complete sketch at the bottom of the page.

Imports and global variables

First, we import some WiFi libraries and define the LED pins. GPIO 13, 12, and 14 are marked D7, D6, and D5 respectively, on the Wemos Mini.

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

const int red_LED_pin = 13;
const int green_LED_pin = 12;
const int blue_LED_pin = 14;

Next, we create an array holding the markup for the form. The HTML is nothing special, but there are a couple of interesting things about this snippet.

First is the PROGMEM modifier in the variable declaration. This makes the array be stored in flash memory instead of SRAM. Flash memory is more abundant, but data stored there is harder to manipulate. Since this array is a constant and it’s a little long, it’s best to store it in flash memory.

The second interesting thing is the R signaling that the array should be read as a raw string literal. This escapes out of any special characters. The ===== at the beginning and end are arbitrary delimiter tokens that let the compiler know where the string begins and ends.

const char MAIN_page[] PROGMEM = R"=====(
  <!DOCTYPE html>
  <html>
  <body>
 
  <form action="/action_page">
    Red: <input type="number" name="red" id="red">
    Green: <input type="number" name="green" id="green">
    Blue: <input type="number" name="blue" id="blue">
    <input type="submit" value="Submit">
  </form> 
 
  </body>
  </html>
)=====";

Next are your WiFi credentials. You’ll need to change these. Giving the internet LED strip a static local IP address will make it easier for our Python script to find it later, so we’ll define that here too. You might have to change those three adresses to work with your router.

//SSID and Password of your WiFi router
const char* ssid = "CHANGETHIS";
const char* password = "CHANGETHISTOO";
const IPAddress ip(192,168,1,213);   
const IPAddress gateway(192,168,1,1);   
const IPAddress subnet(255,255,255,0);

Request handlers

Now we need to make a couple of request handlers. First, a little function to handle serving the page. It just reads the form we made earlier, stores it in a variable (putting it in SRAM), and sends it out responding to the request.

// Serve the page
void handleRoot() {
 String s = MAIN_page; //Read HTML contents
 server.send(200, "text/html", s); //Send web page
}

Second, a function to handle form submissions. This is where the internet LED strip gets lit up. We take the values submitted on POST, make sure they are integers, and send PWM signals on the pins accordingly.

void handleForm() {
 String red_string = server.arg("red"); 
 String green_string = server.arg("green");
 String blue_string = server.arg("blue"); 

 int red_value = red_string.toInt();
 int green_value = green_string.toInt();
 int blue_value = blue_string.toInt();
 
 analogWrite(red_LED_pin, red_value);
 analogWrite(green_LED_pin, green_value);
 analogWrite(blue_LED_pin, blue_value);

 String s = "<a href="/"> Go Back </a>";
 server.send(200, "text/html", s); //Send web page
}

Setup block

Moving on to the setup block, we start our serial, set up a few pins, and start the WiFi service. We already set all of these variables at the top of the script. Counter-intuitively, the static IP is set with the config() function after the WiFi service is started, not before.

WiFi.begin(ssid, password); 
WiFi.config(ip, gateway, subnet);

After some serial prints to help us monitor the WiFi connection, we specify the functions that are to be used for our two pages, the root page, and the form submission page. And then start the server.

server.on("/", handleRoot);
server.on("/action_page", handleForm);
 
server.begin();

Main loop

Finally, we are at the main loop. It’s super simple with just one line that calls the handleClient() function from the WiFi server library. This function will complete the appropriate routing according to the hooks we defined in the setup block.

void loop(void){
  server.handleClient();          //Handle client requests
}

And that’s it for the ESP8266 sketch! Remember, you can find the complete code at the bottom of the page. Also, remember to change the SSID, password, and static IP variables.

You should be able to upload to your board and visit the IP address you defined in your browser. Time to make some pretty colors!


Internet LED strip controller

Next, we’ll be making a simple Python script to read the RGB values from a page on the internet, posted on makerws.com in this instance, and send those values to our internet LED strip. This script is lightweight and perfectly suited to run on an SBC like the Raspberry Pi Zero. Whatever device you end up running it on, it must be on the same local network as the LED strip.

Python environment setup

To get started, first run the following from the terminal to make sure everything is up to date:

sudo apt-get update
sudo apt-get full-upgrade

Now from the home directory, we’ll need to make some new directories. First, a scripts directory to hold our Python script. Second, an env directory to hold files for the Python virtual environment we’ll be using. And finally, a logs directory to keep our error log. Note that this structure is just my preference and may not be the “correct” way. For example, logs are usually stored in the /var directory, but by putting them in our home directory we won’t have to deal with permissions.

mkdir scripts env logs

We will use python3-venv to create our environment. First, install it with:

sudo apt-get install python3-venv -y

Once that is installed, from your home directory, run the following command:

python3 -m venv ./env

If you look in the env directory after the virtual environment is created, you’ll notice that it now contains many files. These are the files needed to run the virtual environment. Again from your home directory, enter the virtual environment with this:

source env/bin/activate

You should now see (env) to the left of your command prompt. Check to see that we are using Python 3 with the following:

python --version

It should say Python 3.x.x. For example, mine says Python 3.7.3. It is now safe to install libraries without worrying about messing up our system. The only library we will need for this project is Beautiful Soup, a webpage scraping utility. Install it with:

pip install beautifulsoup4

We also need to install a parser called <a href="https://lxml.de/index.html">lxml</a> that will be used with Beautiful Soup. There is a pip package for it, but when I tried to run the script, Beautiful Soup didn’t see the installation. So, we have to install it with APT instead:

sudo apt-get install python3-lxml

LED controller script

Now we are ready to start writing the script. The full code is at the bottom of the page. Navigate to the scripts directory we created earlier and start editing the file. If you don’t have a preferred text editor, you can run this command:

nano led_control.py

Imports and global variables

As always, first some imports. We’ll need urllib.request for getting the page from the internet and posting the values to the internet LED strip. BeautifulSoup will let us scrape the values from the page. We will want a delay between page requests so we won’t be constantly sending traffic, so the time library is needed. Last, logging will let us record any errors.

from bs4 import BeautifulSoup
import urllib.request
import time
import logging

Now, let’s set up the logging service. I set the level to warning. The goal is to log any exceptions.

logging.basicConfig(filename='/home/pi/logs/LED.log',level=logging.WARNING)

Next, some global variables. Using the source URL provided below, you can use the Maker WS Cam site to control your LED. If you continue this project to my next guide, you will want to change this to the URL of your hidden page.

Of course, make sure LED_URL matches your internet LED strip. The refresh_time variable sets how many seconds the script will wait between checking the hidden web page.

SOURCE_URL = "https://makerws.com/hidden-page"
LED_URL = "http://192.168.1.213/action_page"
refresh_time = 3

Main loop

Moving on to the main loop, we will first fetch the webpage, then read and parse it with Beautiful Soup:

while 1:
    page_content = urllib.request.urlopen(SOURCE_URL)

    soup = BeautifulSoup(page_content.read(),'lxml')

Next, we’ll use Beautiful Soup to search for the red value. On the Maker WS site, the red value is displayed inside a <p> tag with the ID ‘red’ and that’s what Beautiful Soup will be looking for. Once it finds it, we’ll store it in a variable and then do the same thing for green and blue.

    red_row = soup.find('p', {'id': 'red'})
    red_value = red_row.text

It’s finally time to send the values to the internet LED strip. First, we’ll prepare the request URL as a string. Then, we’ll try to send it using urllib. Sometimes the request times out so we’re going to use a try, except block here:

    post_url = LED_URL + '?red=' + red_value + '&amp;green=' + green_value + '&amp;blue=' + blue_value
        try:
            click_link = urllib.request.urlopen(post_url)

We’ll catch these errors as log them as needed:

        except timeout:
            logging.error('socket timed out - URL %s', url)
        except HTTPError as error:
            logging.error('Data not retrieved because %s\nURL: %s', error, url)
        except URLError as error:
            if isinstance(error.reason, socket.timeout):
                logging.error('socket timed out - URL %s', url)
            else:
                logging.error('some other error happened')

Finally, we’ll wait a few seconds before going back to the top of the loop:

    time.sleep(refresh_time)

Does it work?

Time to test it out! Make sure your internet LED strip is powered on and you can reach it on your local network. Then, making sure you are still in your Python virtual environment on the device where your script is stored, run the script with:

python led_control.py

Now, head over to the cam page on Maker WS, enter some RGB values in the widget on the right, and hit submit. If your LEDs turn the same color as the ones in the live camera feed, then congratulations! Everything is working correctly! If not, time for some troubleshooting. Please ask for help in the comment below.

Make it a service

If you plan on leaving your internet LED strip on indefinitely, you’ll probably want the Python script running in the background. While some people may suggest using Crontab, I find systemd to be much more reliable for always-on services.

To turn your Python script into a system service, first, open a new file using your text editor of choice or Nano:

sudo nano /etc/systemd/system/led-control.service

Post the following code in the file. Make sure the paths in the [Service] section are all correct, save, and exit the text editor.

[Unit]
Description=Led controller
After=network.target

[Service]
User=pi
Group=pi
WorkingDirectory=/home/pi/scripts/
Environment="PATH=/home/pi/env/bin"
ExecStart=/home/pi/env/bin/python /home/pi/scripts/led_control.py
Restart=on-failure

[Install]
WantedBy=multi-user.target

Now, start the service with the following command:

sudo systemctl start led_control.service

Then check to see if it is running all right with this:

sudo systemctl status led_control.service

As long as you see something like this:

You’re good to go. Make the service start at boot with the following:

sudo systemctl enable led_control.service

You might as well give it another test run with the RGB widget on our cam page.

From here, you can either continue to use this website to control your internet LED strip or continue on to learn how to control it from your own WordPress site.


Take the internet LED strip to the internet

WordPress (WP) is an open-source fully-customizable content management platform that makes building a website as easy or as difficult as you want it to be. It’s no wonder that it powers 35% of the internet.

If you don’t already have a WordPress site running, there are many options to get one. By far the easiest is using a hosting solution like Hostinger (not affiliated) to install and start up a WP instance for you.

If you are trying to save money or just want to tinker around, you could install WP on a local computer. You can either install a LAMP stack and then download and configure WP, or just simply spin up a WP Docker container.

Once you have a WP site running, you only need to do a few things to prepare it to control our internet LED strip. First, make a table in the WP database to hold the RGB values. Then, make a page to display the values so they can be read by your LED controller. Finally, create a plugin so we can change the values from the website.

Let’s get to it!


WordPress database table

In this section, we will create a table in the WP database to store the RGB values that the LED strip will read.

All the data on a WP site is stored in a MySQL database. There are several ways to manipulate the database from the backend, but by far the easiest way is to use phpMyAdmin.

If you are using a hosting service for your site, then there is probably a phpMyAdmin link in your dashboard. Otherwise, you will need to install it from your terminal or add it to your Docker compose file. If you have any trouble, please ask for help in the comments below.

First, log in to phpMyAdmin. My password is p@$$word. Yours may or may not be different.

phpMyLogin login gonna get that internet led strip working
O Admin! My Admin!

Next, press the plus symbol to expand your database (wordpress in my case) and click on New:

new db table gonna get that internet led strip working
This is almost too easy

Making the table is not too difficult, but this is the most complicated part. Don’t worry though, if you mess it up you can delete it and try again. First, give the table a name at the top of the form. Then add the columns:

some columns in phpMyAdmin gonna get that internet led strip working
Columns may vary

For the LED strip, we’ll at least need a column each for red, green, and blue. In case you want to have more than one strip in the future, it would be nice to have a name for this strip, so a device name and device id columns are needed. And why not have a device ID. Finally, I would want to know how long my power consuming light monster has been on, so let’s include a timestamp to see when the last time the strip was ‘touched’ was.

After you are happy with your columns, hit the save button. You should see something like this:

New columns in phpMyAdmin gonna get that internet led strip working
It’s alive!

Check the box next to device_id and click the primary button to make it the main index.

The table is ready to go! But it’s empty. Lets put some dummy data in it for now. Click the SQL button in the menu bar at the top of the page. Then, enter the following query, making sure the table name and column names match yours, and click Go.

INSERT INTO LED_strip (device_id, device_name, red, green, blue, last_change) VALUES (0, 'led_strip', 255, 255, 255, CURRENT_TIMESTAMP);

The data should have been added to your table and you should see something like this:

phpmyadmin values updated gonna get that internet led strip working
Yay!

Hidden page in WordPress

If you make any changes or add any files inside your theme directory, they will be lost the next time you update the theme. I learned this the hard way. The solution to this problem is to create a child theme.

Make a child theme

A child theme is just a theme that inherits all of the styling and pages of its parent theme. You can add to or modify the parent theme through the child theme, and when the parent theme needs to be updated, none of your changes will be overwritten.

We will need to create a new page for our internet LED strip, so we will need to create a child theme. But the best part is they are super simple to create. It only takes four steps.

It would be prudent to make a backup of your site before moving forward.

First, create a new folder in your themes folder. WordPress convention says that you should name the child theme and its folder the same name as the parent theme with ‘-child’ appended. My parent theme is Astra so while in the themes directory I would run this command:

mkdir astra-child

For the second and third steps, we need to create two files to make the new child theme usable. Create a style.css file inside the child theme folder you just created and insert something similar to the following inside:

/*
Theme Name: Astra Child
description: Astra Child Theme
Author: R Mays
Template: astra
Version: 1.0.0
*/

The above is the bare minimum you need in the style.css file. If you plan on sharing the theme, you could add your personal URL, license info, etc. After you make your style.css look something like the above (change the info to match your parent theme) it’s time to make the other file, functions.php .

The following code works with the Astra theme, but you may need to make some changes to make it work your theme. It all depends on how your theme loads its stylesheets. More information can be found at WordPress.org.

This is what my functions.php looks like:

<?php 
add_action( 'wp_enqueue_scripts', 'enqueue_parent_styles' );

function enqueue_parent_styles() {
   wp_enqueue_style( 'parent-style', get_template_directory_uri().'/style.css' );
}

Now to complete the fourth and final step, go to your WP admin dashboard. Go to ‘Appearance’ and ‘Themes’. You should see your new child theme there. Activate it.

Now, if everything went correctly, you should be able to visit your site and it should look exactly like it did before.

Create a template for hidden page

Now that we have a child-theme, we can create a template for our hidden page without having to worry about it being erased the next time our main theme is updated. There will be a bit of coding here. The complete files can be found at the bottom.

The easiest way to make a new template is to make a copy of the default one provided by our parent theme. So, while in the child theme directory, run the following command (change it to match your parent theme):

cp ../astra/page.php ./hidden-page.php

We will make four changes to the base template to make it query the database and secretly display the RGB values for our internet LED strip to eventually use.

First, a little housekeeping, we need to change the metadata at the top of the file so WP knows that this is a template. In hidden-page.php, replace the opening PHP tag and comment with the following (of course make changes so it matches your child theme):

<?php /**
 *  Template Name: Data Page
 *
 *  @package WordPress
 *  @subpackage Astra Child
 *  @since Astra Child 1.0
 */

For the second change, we will add a helper function that will calculate how long it was since the last change to the RGB values was. It takes the last access time as a parameter, finds the difference from the present time in seconds, and then returns that period as minutes. Insert this directly after the line that says ‘get_header()’.

<?php function getTimeDifference($last_access) {
		$last_access = new DateTime($last_access);
		$elapsed_time = $last_access->diff(new DateTime("now"));
		$minutes_passed = $elapsed_timedays * 24 * 60;
		$minutes_passed += $elapsed_time->h * 60;
		$minutes_passed += $elapsed_time->i;

		return $minutes_passed;
	}
?>

Next, initialize some variables to hold the RGB values and then query the database for those values. We’ll use the function we just defined to make sure the last change happened within the past five minutes. You put this code block directly after the getTimeDifference() function:

<?php
	$red = 0;
	$green = 0;
	$blue = 0;

	require( ABSPATH. '/wp-load.php' );
	global $wpdb;
	$results = $wpdb->get_row($wpdb->prepare(" SELECT * FROM LED_strip WHERE device_name='LED_Strip'"));
	if (!empty($results)) {
		$mins_since_last_change = getTimeDifference($results->last_access);
		
		if ($mins_since_last_change < 6) {
			$red = $results->int_one;
			$green = $results->int_two;
			$blue = $results->int_three;
		}
	}
?>

The fourth and final step is to insert the values into the body of the page. These values are going to be hidden, so it doesn’t matter too much where you put them. In my theme, there is a div with the class “primary”. Putting them anywhere in there should work nicely. While the placement isn’t important, each value must have a unique ID.

<p hidden id="red"><?php echo($red); ?></p>
<p hidden id="green"><?php echo($green); ?></p>
<p hidden id="blue"><?php echo($blue); ?></p>

Make the hidden page

That’s it for the code for the template, now let’s make it a page. Go into you the WP-admin dashboard, click on “Pages” and then “Add new”. Give the page a title, and then look for the “Templates” dropdown in the left sidebar. Mine is called “Data Page”. Select that and save the page.

If you visit the page and see nothing except a title and the basic blocks of your WP theme, great! If you get an error, you can look for DEBUG in your wp-admin.php file and change its value to true (make sure to change it back later).

Just to make sure everything is working right, you can view page source with Ctrl+u and search for the value by the ID we gave it earlier with Ctrl+f (Option+Command+u and Command+f on Mac). If you see the values (they should all be 0 by now), then great! Time to move on to the next step for this internet LED strip, a plugin to control it.


WP widget plugin

Create the main plugin file

Creating a plugin is easy. Simply create a new folder in your plugins folder and make a PHP file with the same name as the folder. In my case, I have a folder named LEDcontrol a file called LEDcontrol.php in it. Add something like this to the top of the file:

<?php
   /*
   Plugin Name: LED Control
   Plugin URI: https://makerws.com
   description: A plug to store RGB values in db
   Version: 1.2
   Author: R Mays 
   Author URI: http://makerws.com
   License: GPL2
   */

Now we have a plugin! But it doesn’t do anything. Let’s put a widget in it. I followed this great guide. Copy their sample code and change all the name references to something that will match our internet LED strip. For example, wpb_widget could become wpled_widget. Besides editing the name, there are two big changes we need to make.

First, we need to make a form where visitors can input the RGB values. Look for and erase the line that contains the text “Hello World!” and insert this code to create our form:

echo __( 
	'<form method="post" name="LEDform">
	<p> Please enter RGB values (0 - 255) </p>
	<p>R: <input id="red" type="number" min="0" max="255" name="red"></p>
	<p>G: <input id="green" type="number" min="0" max="255" name="green"></p>
	<p>B: <input id="blue" type="number" min="0" max="255" name="blue"></p>
	<input type="hidden" name="led_action" value="xyza">
	<input type="submit" value="Submit">
	</form>
	', 'wpled_widget_domain' 
);

As it is right now, we have a form, but nothing will happen when we push the submit button. We need to attach an action to it. So, at the end of the file (but before the closing ?&gt; tag) insert this:

add_action( 'init', function() {
    if ( empty( $_POST['led_action'] ) ) {
        return;
    }

    include WP_CONTENT_DIR. "/plugins/LEDcontrol/processLED.php"; 
});

Make a helper file

You may have noticed that there is an include statement for a file that doesn’t exist. That’s because we haven’t made it yet. Let’s make another file and add code to handle the form submission.

<?php
$datenow=date("Y-m-d H:i:s"); 

$red_string=$_POST["red"];
$green_string=$_POST["green"];
$blue_string=$_POST["blue"];

$red= (int)$red_string * 4;
$green= (int)$green_string * 4;
$blue= (int)$blue_string * 4;

$device_id=0;
$device_name='LED_Strip';

if (($red >= 0 && $red <= 1020) && ($green >= 0 && $green <= 1020) && ($blue >= 0 && $blue <= 1020)) {
	require( ABSPATH. '/wp-load.php' );
	global $wpdb;
	$wpdb->query( $wpdb->prepare(
	    "
		REPLACE INTO LED_strip
		( device_id, device_name, int_one, int_two, int_three, last_access )
		VALUES ( %s, %s, %s, %s, %s, %s )
	    ",
	    $device_id,
	    $device_name,
	    $red,
	    $green,
	    $blue,
	    $datenow
	));
}
?>

Save the file as processLED.php in the same folder as LEDcontrol.php. All this piece of code is doing is replacing the old data in the database with the new values. Before we send the RGB values, though, we have to make sure they are integers and get them ready for the internet LED strip’s analogWrite() function.

Because we specified the input type as numeric in the HTML markup, the values the user submitted should be integers, but to make sure lets cast them as integers by typing (int) before the variable name.

But why multiple the values by four after casting them as integers? Normally RGB values range from 0 to 255. But for the ESP8266, the analogWrite() function takes values between 0 and 1023. Multiplying by 255 by four will give us a max of 1020, which is close enough to full power.

Wrapping up

Those two files are all you need for the plugin. You should be able to activate the plugin from your WP-admin dashboard. After it is activated, head to “Widgets” under the “Appearance” tab and add the widget to your sidebar.

Try submitting values a few times and viewing the hidden page’s source code to see if the values are being updated. If they are, you’re done!


Complete files

Internet LED strip ESP8266 sketch

ESP8266internetLEDstrip.ino

/*
 * Based of form from: ESP8266 (NodeMCU) Handling Web form data basic example
 * <a href="https://circuits4you.com">https://circuits4you.com</a>
 */
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

const int red_LED_pin = 13;
const int green_LED_pin = 12;
const int blue_LED_pin = 14;

const char MAIN_page[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<body>
 
<form action="/action_page">
  Red: <input type="number" name="red" id="red">
  Green: <input type="number" name="green" id="green">
  Blue: <input type="number" name="blue" id="blue">
  <input type="submit" value="Submit">
</form> 
 
</body>
</html>
)=====";
 
//SSID and Password of your WiFi router
const char* ssid = "MySecureRouter";
const char* password = "admin";
const IPAddress ip(192,168,1,213);   
const IPAddress gateway(192,168,1,1);   
const IPAddress subnet(255,255,255,0);   
   
ESP8266WebServer server(80); //Server on port 80
 

// Serve the page
void handleRoot() {
 String s = MAIN_page; //Read HTML contents
 server.send(200, "text/html", s); //Send web page
}


void handleForm() {
 String red_string = server.arg("red"); 
 String green_string = server.arg("green");
 String blue_string = server.arg("blue"); 

 int red_value = red_string.toInt();
 int green_value = green_string.toInt();
 int blue_value = blue_string.toInt();
 
 analogWrite(red_LED_pin, red_value);
 analogWrite(green_LED_pin, green_value);
 analogWrite(blue_LED_pin, blue_value);

 String s = "<a href='/'> Go Back </a>";
 server.send(200, "text/html", s); //Send web page
}


void setup(void){
  Serial.begin(9600);

  pinMode(red_LED_pin, OUTPUT);
  pinMode(green_LED_pin, OUTPUT);
  pinMode(blue_LED_pin, OUTPUT);
  
  WiFi.begin(ssid, password);     //Connect to your WiFi router
  WiFi.config(ip, gateway, subnet);
  
  Serial.println("");
 
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  //If connection successful show IP address in serial monitor
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println("WiFi");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());  //IP address assigned to your ESP
 
  server.on("/", handleRoot);      //Which routine to handle at root location
  server.on("/action_page", handleForm); //form action is handled here
 
  server.begin();                  //Start server
  Serial.println("HTTP server started");
}

void loop(void){
  server.handleClient();          //Handle client requests
}

LED controller Python script

led_control.py

from bs4 import BeautifulSoup
import urllib.request
import time
import logging

logging.basicConfig(filename='/home/ben/logs/LED.log',level=logging.WARNING)

SOURCE_URL = "https://makerws.com/hidden-page"
LED_URL = "http://192.168.1.213/action_page"

while 1:
    page_content = urllib.request.urlopen(SOURCE_URL)

    soup = BeautifulSoup(page_content.read(),'lxml')

    red_row = soup.find('p', {'id': 'red'})
    red_value = red_row.text

    green_row = soup.find('p', {'id': 'green'})
    green_value = green_row.text

    blue_row = soup.find('p', {'id': 'blue'})
    blue_value = blue_row.text

    post_url = LED_URL + '?red=' + red_value + '&amp;green=' + green_value + '&amp;blue=' + blue_value
    try:
        click_link = urllib.request.urlopen(post_url)
    except timeout:
        logging.error('socket timed out - URL %s', url)
    except HTTPError as error:
        logging.error('Data not retrieved because %s\nURL: %s', error, url)
    except URLError as error:
        if isinstance(error.reason, socket.timeout):
            logging.error('socket timed out - URL %s', url)
        else:
            logging.error('some other error happened')

    time.sleep(3)

Controller systemd unit file

led-control.service

[Unit]
Description=Led controller
After=network.target

[Service]
User=pi
Group=pi
WorkingDirectory=/home/pi/scripts/
Environment="PATH=/home/pi/env/bin"
ExecStart=/home/pi/env/bin/python /home/pi/scripts/LED_control.py
Restart=on-failure

[Install]
WantedBy=multi-user.target

Child theme

CSS file

style.css

/*
Theme Name: Astra Child
description: Astra Child Theme
Author: Ben Mays
Template: astra
Version: 1.0.0
*/

Functions file

functions.php

<?php

add_action( 'wp_enqueue_scripts', 'enqueue_parent_styles' );

function enqueue_parent_styles() {
   wp_enqueue_style( 'parent-style', get_template_directory_uri().'/style.css' );
}

Page Template

hidden-page.php

<?php /**
 *  Template Name: Data Page
 *
 *  @package WordPress
 *  @subpackage Astra Child
 *  @since Astra Child 1.0
 */
if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

get_header(); ?>

<?php 
	function getTimeDifference($last_access) {
		$last_access = new DateTime($last_access);
		$elapsed_time = $last_access->diff(new DateTime("now"));
		$minutes_passed = $elapsed_time->days * 24 * 60;
		$minutes_passed += $elapsed_time->h * 60;
		$minutes_passed += $elapsed_time->i;

		return $minutes_passed;
	}
?>

<?php 
	$red = 0;
	$green = 0;
	$blue = 0;

	require( ABSPATH. '/wp-load.php' );
	global $wpdb;
	$results = $wpdb->get_row($wpdb->prepare(" SELECT * FROM LED_strip WHERE device_name='LED_Strip'"));
	if (!empty($results)) {
		$mins_since_last_change = getTimeDifference($results->last_access);
		
		if ($mins_since_last_change < 6) {
			$red = $results->int_one;
			$green = $results->int_two;
			$blue = $results->int_three;
		}
	}

?>
<?php if ( astra_page_layout() == 'left-sidebar' ) : ?>

	<?php get_sidebar(); ?>

<?php endif ?>

	<div id="primary" <?php astra_primary_class(); ?>>

		<?php astra_primary_content_top(); ?>

		<?php astra_content_page_loop(); ?>

		<?php astra_primary_content_bottom(); ?>
		<p hidden id="red"><?php echo($red); ?><p>
		<p hidden id="green"><?php echo($green); ?><p>
		<p hidden id="blue"><?php echo($blue); ?><p>

	</div><!-- #primary -->

<?php if ( astra_page_layout() == 'right-sidebar' ) : ?>

	<?php get_sidebar(); ?>

<?php endif ?>

<?php get_footer(); ?>

Plugin

Main file

LEDcontrol.php

<?php
   /*
   Plugin Name: LED Control
   Plugin URI: https://makerws.com
   description: A plugin to store RGB values in db
   Version: 1.2
   Author: R Mays 
   Author URI: http://makerws.com
   License: GPL2
   */

// Creating the widget
class wpled_widget extends WP_Widget {

function __construct() {
parent::__construct(

// Base ID of your widget
'wpled_widget',

// Widget name will appear in UI
__('LED Control', 'wpled_widget_domain'),

// Widget description
array( 'description' => __( 'A widget to update RGB values in the db', 'wpled_widget_domain' ), )
);
}

// Creating widget front-end

public function widget( $args, $instance ) {
$title = apply_filters( 'widget_title', $instance['title'] );

// before and after widget arguments are defined by themes
echo $args['before_widget'];
if ( ! empty( $title ) )
echo $args['before_title'] . $title . $args['after_title'];

// This is where you run the code and display the output
echo __( 
	'<form method="post" name="LEDform">
	<p> Please enter RGB values (0 - 255) </p>
	<p>R: <input id="red" type="number" min="0" max="255" name="red"/></p>
	<p>G: <input id="green" type="number" min="0" max="255" name="green"/></p>
	<p>B: <input id="blue" type="number" min="0" max="255" name="blue"/></p>
	<input type="hidden" name="led_action" value="xyza"/>
	<input type="submit" value="Submit" />
	</form>
	', 'wpled_widget_domain' 
	);
echo $args['after_widget'];
}

// Widget Backend
public function form( $instance ) {
if ( isset( $instance[ 'title' ] ) ) {
$title = $instance[ 'title' ];
}
else {
$title = __( 'New title', 'wpled_widget_domain' );
}
// Widget admin form
?>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
</p>
<?php
}

// Updating widget replacing old instances with new
public function update( $new_instance, $old_instance ) {
$instance = array();
$instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
return $instance;
}

// Class wpled_widget ends here
}


// Register and load the widget
function wpled_load_widget() {
    register_widget( 'wpled_widget' );
}
add_action( 'widgets_init', 'wpled_load_widget' );

add_action( 'init', function() {
    if ( empty( $_POST['led_action'] ) ) {
        return;
    }

    // handle form submission
    include WP_CONTENT_DIR. "/plugins/LEDcontrol/processLED.php"; // Replace xxx_insert.php with exact path to php file
});

?>

Helper file

processLED.php

<?php
$datenow=date("Y-m-d H:i:s"); 

$red_string=$_POST["red"];
$green_string=$_POST["green"];
$blue_string=$_POST["blue"];

$red= (int)$red_string * 4;
$green= (int)$green_string * 4;
$blue= (int)$blue_string * 4;

$device_id=0;
$device_name='LED_Strip';

if (($red >= 0 && $red <= 1020) && ($green >= 0 && $green <= 1020) && ($blue >= 0 && $blue <= 1020)) {
	require( ABSPATH. '/wp-load.php' );
	global $wpdb;
	$wpdb->query( $wpdb->prepare(
	    "
		REPLACE INTO LED_strip
		( device_id, device_name, int_one, int_two, int_three, last_access )
		VALUES ( %s, %s, %s, %s, %s, %s )
	    ",
	    $device_id,
	    $device_name,
	    $red,
	    $green,
	    $blue,
	    $datenow
	));
}

?>

4 thoughts on “How to Make an Internet LED Strip with ESP8266, Python, and WordPress”

  1. Pingback: Internet LEDs! - Threw the Looking Glass

  2. Pingback: Control LED from WordPress Site – Full-Stack Feed

  3. Hey! I just wish to give a huge thumbs up for the great information you’ve gotten right here on this post. I might be coming again to your weblog for extra soon.

Leave a Comment

Your email address will not be published. Required fields are marked *