element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      • Japan
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Vietnam
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Bluetooth Unleashed Design Challenge
  • Challenges & Projects
  • Design Challenges
  • Bluetooth Unleashed Design Challenge
  • More
  • Cancel
Bluetooth Unleashed Design Challenge
Blog Universal LED Animator #6 - Basic web interface
  • Blog
  • Forum
  • Documents
  • Polls
  • Files
  • Events
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: mgal
  • Date Created: 11 Jul 2018 11:04 AM Date Created
  • Views 603 views
  • Likes 8 likes
  • Comments 2 comments
  • ble
  • python programming
  • flask
  • web interface
Related
Recommended

Universal LED Animator #6 - Basic web interface

mgal
mgal
11 Jul 2018

Hello and welcome to my sixth update on the Universal LED Animator. This time I'm going to describe the basic structure of the Animator's web interface. As a quick refresher, my Hero boards (Redbear Blends) are connecting via BLE to the Beagle Bone Black Wireless which serves as the central unit for the system and provides users with a web interface to configure the LED patterns on the Animators. Naturally we can do more than only setting up patterns - we also want to be able to change animation speeds, brightness, and colours. At the moment I have a basic, no-style webpage providing pattern selection buttons and a brightness slider for a single Animator device:

image

Let's see how I got there...

 

  • What I had already
  • Flask
  • JavaScript
  • Video proof
  • Up next

 

What I had already

In my last week's quickie update I gave an explanation of the Bluefruit library and why I preferred it over the GATT SDK. With a basic script connecting to the Blend and pushing my super-simple Animator protocol over BLE I could start adding features and connecting them to the web interface. For now I've only been working with a single device and more will be added after the interface is full-featured.

 

Flask

In order to serve a website, I needed a Python Web framework. A web framework is a scaffolding that will allow me to build interactive websites and script around their features, allowing me to connect interface widgets like buttons to physial actions, like changing the LED strip brightness. I decided to pick up Flask, an April-Fools-joke-turned-microframework, for its extreme simplicity, ease of use and flat learning curve. As is often the case with Python web features, Flask's minimal application is very concise, consisting of five actual lines of code:

 

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
  return 'Hello, World!'

At the same time, it's very easy to expand it and create a decent web interface for an embedded application - Flask is commonly used to create robot or smart building interfaces. As I have virtually no experience in web development, those were all very important characteristics for me. The main Flask-related code was developed over a few days and for now consists of only two functions, one for a slider and another for buttons:

 

#Create the command queue for inter-process communication and add the first command.                            
queue = multiprocessing.Queue()
queue.put('Q')

#Serve the pattern buttons.  
@app.route("/")
@app.route("/<state>")
def update_mode(state=None):
    global queue
    print "Mode changed."
    queue.put('m'+chr(ord(state)))
    template_data = {
        'title' : state,
    }
    html = open("index.html")
    response = html.read().replace('\n', '')
    html.close()
    return response

#Serve the brightness slider.
@app.route("/set_brightness")
def set_brightness():
  brightness = request.args.get("brightness")
  print "Brightness: " + str(brightness)
  queue.put('b'+str(brightness))

 

First, I create a Queue object that will be used to exchange commands between the Flask app and a Bluefruit loop running in the background - more on that later. Then I serve the buttons for changing the pattern. Each of the 10 buttons is a link taking us to the same main website, but with a URL extended by the pattern number. This URL change is intercepted by Flask and triggers the update_mode method which reads the URL route and puts it in a variable that is added to the queue. The other function deals with the brightness slider, which sends GET requests on every value change. Those requests are then read by the URL processor in Flask and a method is executed, once again passing the value to the command queue. The command queue is read in an endless loop inside the Bluefruit subprocess, and each item is dequeued and sent over the BLE link to be parsed inside the Arduino firmware:

 

print("Waiting for commands.")
            # Wait forever for commands from the queue.
            while 1:
                if not (q.empty()):
                    print("TXed.")
                    msg = q.get()
                    uart.write(msg)

 

The complete code is below:

import Adafruit_BluefruitLE
from Adafruit_BluefruitLE.services import UART
from itertools import cycle
from flask import Flask, render_template, request
import multiprocessing
from distutils.log import debug
import sys

app = Flask(__name__)

def loop_a(q):    
    # Get the BLE provider for the current platform.
    ble = Adafruit_BluefruitLE.get_provider()
    
    #Main function to run all BLE-related logic
    def main():
        #Get a clean start
        ble.clear_cached_data()
        adapter = ble.get_default_adapter()
        adapter.power_on()
        print('Using adapter: {0}'.format(adapter.name))
        print('Disconnecting any connected UART devices...')
        UART.disconnect_devices()
     
        # Scan and connect to the first UART device you can find:
        print('Searching for UART device...')
        try:
            adapter.start_scan()
            device = UART.find_device()
            if device is None:
                raise RuntimeError('Failed to find an LED Animator')
                sys.exit()
        finally:
            adapter.stop_scan()
    
        print('Connecting to device...')
        device.connect()
        
        try:
            # Discover services
            print('Discovering services...')
            UART.discover(device)
            uart = UART(device)
    
            print("Waiting for commands.")
            # Wait forever for commands from the queue.
            while 1:
                if not (q.empty()):
                    print("TXed.")
                    msg = q.get()
                    uart.write(msg)
                                  
        finally:
                            
            device.disconnect()
    
    
    # Initialize BLE
    ble.initialize()
    
    # Run BLE loop
    ble.run_mainloop_with(main)


#Create the command queue for inter-process communication and add the first command.                                 
queue = multiprocessing.Queue()
queue.put('Q')

#Serve the pattern buttons.  
@app.route("/")
@app.route("/<state>")
def update_mode(state=None):
    global queue
    print "Mode changed."
    queue.put('m'+chr(ord(state)))
    template_data = {
        'title' : state,
    }
    html = open("index.html")
    response = html.read().replace('\n', '')
    html.close()
    return response

#Serve the brightness slider.
@app.route("/set_brightness")
def set_brightness():
  brightness = request.args.get("brightness")
  print "Brightness: " + str(brightness)
  queue.put('b'+str(brightness))

        
#Start a background Bluefruit process and launch the Flask app.      
def main():
    multiprocessing.Process(target=loop_a, args=(queue,)).start()
    app.run(host= '0.0.0.0', debug=False, port=81)
    

main()
print "Loop ends."

 

As you can see, I used Python's very straightforward Multiprocessing capability to integrate Bluefruit's features with the Flask web app.  Before Flask is run (line #95), a Multiprocessing process is created, running the Bluefruit's loop in the background, parallel to the main Flask app. The queue object is passe to the subprocess, allowing information exchange by adding items to the queue to be interpreted by either process. I only use one-way communication at the moment, but will expand it to two-way at some point, as some info needs to be returned by the Animators to the main app.

 

Code was created based on the book Beaglebone by Example and this Instructable.

 

JavaScript

My website's source looks like this:

<!DOCTYPE html>
<html>
<body>

<h1>Universal LED Animator</h1>

<div id="slidecontainer">
  <input type="range" min="0" max="255" value="0" id="brightness">
  <p>Brightness: <span id="brightness_value"></span></p>
</div>

<script>
var slider = document.getElementById("brightness");
var output = document.getElementById("brightness_value");
output.innerHTML = slider.value;

slider.oninput = function() {
  output.innerHTML = slider.value;
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
    }
  };
  xhttp.open("GET", "/set_brightness?brightness=" + slider.value, true);
  xhttp.send();
}
</script>
<p>Pattern:</p>
<a href="/1" id="off"> <button type="button">1</button> </a>
<a href="/2" id="off"> <button type="button">2</button> </a>
<a href="/3" id="off"> <button type="button">3</button> </a>
<a href="/4" id="off"> <button type="button">4</button> </a>
<a href="/5" id="off"> <button type="button">5</button> </a>
<a href="/6" id="off"> <button type="button">6</button> </a>
<a href="/7" id="off"> <button type="button">7</button> </a>
<a href="/8" id="off"> <button type="button">8</button> </a>
<a href="/9" id="off"> <button type="button">9</button> </a>

</body>
</html>

 

Slider code was taken from this W3C example page. Line #20 is where the actual action happens, giving our app realtime responsiveness - as soon as the user changes the slider's position, an HTTP GET is immediately sent out so that Flask can act on it and send commands to the device. Actually, there is too many requests to process sometimes and the BLE link gets congested - I'll work on this problem before the final release. The slider code is also responsible for the plain text readout of the brightness value below the slider, which is extremely useful for debugging and also informative for the end user. Below the slider script there are the LED pattern buttons, created using plain HTML.

 

Video proof

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

 

Up next

Having achieved this milestone and completed the BLE part of the script, I will now focus on adding pattern-editing features to the web interface, putting some proper style on it, and creating multi-device functionality. OpenCV development is shelved at the moment as I encountered plenty of obstacles and I want to focus on posting updates more often.

  • Sign in to reply
  • aspork42
    aspork42 over 7 years ago

    Great update! I need to implement this on my LED strips...

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • DAB
    DAB over 7 years ago

    Very well written update.

     

    DAB

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube