Building a Python Flask Web UI For Raspberry Pi Sure Elec LCD

In an earlier post I outlined how I setup a Sure Electronics LCD screen with my Raspberry Pi 3 using a Python driver. Whilst updating the LCD via command line is immensely useful I decided to build a UI to control the LCD send messages too it. By using a browser based UI I could update the LCD screen from anywhere. Essentially this was a chance to play with a Python web framework and write some code!

BlogFlaskUISureElecLCD2 

I’ve passed the UI’s URL round my family’s devices at home and they now send me messages whilst I’m in my study working/playing.

The end result can be found in my GitHub repo.

As my driver was in Python and I’m enjoying coding in Python at the moment I decided to use a Python Web framework to serve the HTML/JavaScript UI and host RESTful services on the server side to accept LCD commands. After some reading I went with Flask which  seemed perfect for my needs. I could have gone with Django but Flask seemed for appropriate for my needs. For a good comparison see this CodeMentor.io post. For a great tutorial on Flask checkout this series by Miguel Grinberg and this great post by Scotch.io.  

Building the server side web framework was easy and logical in Flask and I was able to get something setup in one file which served my needs. However after reading some Flask best practices I spread my solution out into a more appropriate structure. Flask will seem familiar to web developers with experience of ASP.net MVC, Web API, Node/Express etc. You define routes to handle incoming requests. The key aspects my solution are outlined below. I am running the Flask server directly on my Raspberry Pi and using it to serve the pages and host the services for commanding the LCD screen.

To install Flask (on a Pi) first install Python Pip (a popular Python Package Manager) via “apt-get install python-pip” or “apt-get install python3-pip” (for a Python v3 specific Pip) and then install Flask via “pip install flask”.

Flask comes with a small lightweight development server which runs your app in Dev mode and also auto restarts after code changes. I found this fast and robust enough for my needs. 

Lets check out the main parts of the code:

run.py:  This is the entry point for the app. When run it calls run in the app file and here I have optionally passed IP/Port I want the app to run on which enables the app to be exposed to the internal network so I can connect from other machines on the network. 

from app import app 
if __name__ == '__main__':
    app.run(host="192.100.100.100", port=5000)

app/__inti_.py & config.py: This is the app initialisation code and where it points to the config.py file where config settings can be set for the app.

app/views.py: This is the heart of the app. After importing the relevant python components and instantiating the Smartie LCD driver (from previous post), the routes for the app are defined.

@app.route("/")
def show_homepage():
    return "Home Page!"

@app.route("/lcd")
def show_lcdpage():
    name="Jeff"
    return render_template("lcd.html", name=name)

The route for root will just return the text “Home Page” whereas the route for /lcd will call render_template to return a templated HTML page (lcd.html) and passes any relevant data (e.g. “Jeff” which is irrelevant in this example”). Templates will be covered shortly below.

@app.route("/lcd/clear", methods=["POST","GET"])
def display_clear():
    smartie1.clear_screen()
    return "Success"

@app.route("/lcd/displaymessage", methods=["POST"])
def display_message():
    if not request.json:
        abort(400)
    smartie1.clear_screen()
    smartie1.backlight_on()
    smartie1.write_lines_scroll(request.json['Lines'])
    return "Success" 

Any POST or GET on http://SERVERADDRESS:PORT/lcd/clear will result in the smartie drivers clear screen method being called. A POST to “/lcd/displaymessage” will be validated to ensure that the request contains JSON data and then the data will be passed to the driver for display.

/app/templates/lcd.html: This is the main HTML page that enables the user to type the messages to display.

BlogFlaskUI2

The CSS and JavaScript used by this page is found in the static folder and referenced in the usual way………….

<!-- CSS for our app -->         
 <link rel="stylesheet" href="/static/lcd.css"/>

<!-- JS for our app --> 
<script type="text/javascript" src="/static/lcd.js" charset="utf-8"></script>

So we need to ensure that the flask server returns these static files, but we don’t want to have to define  a specific app.route for each one so instead we use this one in our views.py :

@app.route('/<path:filename>')  
def send_file(filename):  
      return send_from_directory('/static', filename)

This basically states that any requests for a file path are sourced from the /static folder directly. So we can just place any files in the static folder that we want to be served directly (the CSS and JavaScript files in our case).

/app/static/lcd.js:  From this JavaScript code we can consume the services hosted by Flask for our application. It’s using the XMLHttpRequest object to make AJAX requests to the Flask server. The SendCommand function takes callback methods which will be called on success or error.

function SendCommand(url, httpVerb, data, successCallback, errorCallback){
                
  var dataToSend;
  if(data!=null){
      var dataToSend = JSON.stringify(data);          
  }
   var request = new XMLHttpRequest();
  request.open(httpVerb, url, true);
  request.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
   request.onload = function() {
      if(this.status >= 200 && this.status < 400){
          // success here
          var returnedData; 
          if (this.response != null){
              successCallback(returnedData, this.status);
          }                                                                        
      }
      else{
          //error returned from server
          errorCallback("Error response returned from server", this.status);
      }
  }
   request.onerror = function() {
          errorCallback("Error contacting server", this.status);
  }

  if (dataToSend != null){
      request.send(dataToSend);
  }
  else{
      request.send();
  }
}

That’s mostly it. Run the app by running the run.py module (e.g in the Python IDLE or terminal) and direct your browser to http://SERVERADDRESS:5000/lcd.

The code for my Python driver and this web app is available on GitHub here https://github.com/RichHewlett/smartie and https://github.com/RichHewlett/LCD-Smartie-Web.

Useful React.JS Learning Resources

Useful React.JS Learning Resources

Below are some links that you might find useful for learning React.js and Flux, Facebook’s successful JavaScript UI framework.  There are a lot of resources out there but here are some of the best that I have collected for members of my team.

Introductions and overviews of React.js:

Flux:

Tutorials for Flux & React:

Books: 

Prefer the old school approach of reading a book then instead check out this: React.js Essentials by Artemij Fedosejev

NPM config for web access via a proxy

NPM config for web access via a proxy

If you are using NPM for to install your JavaScript modules and you are sitting behind a corporate proxy server  with a strict firewall then you will likely be having problems. If NPM cannot find its way out to the web you will likely be getting a timeout error like the one below:

npm-logo

npm ERR! argv “C:\\node.exe” “C:\\nodejs\\node_modules\\npm\\bin\npm-cli.js” “install” “package1”
npm ERR! node v4.2.1
npm ERR! npm  v2.14.7
npm ERR! code ETIMEDOUT
npm ERR! errno ETIMEDOUT
npm ERR! syscall connect
npm ERR! network connect ETIMEDOUT 185.31.18.162:443
npm ERR! network This is most likely not a problem with npm itself
npm ERR! network and is related to network connectivity.
npm ERR! network In most cases you are behind a proxy or have bad network settings.
npm ERR! network
npm ERR! network If you are behind a proxy, please make sure that the
npm ERR! network ‘proxy’ config is set properly.  See: ‘npm help config’

To resolve this problem you need to tell NPM the address of your web proxy, including the username/password to authenticate, so that it can route outgoing HTTP requests via that proxy. NPM stores its configuration in a config file and can be edited via the console/terminal using “NPM Config” command. Use this command to set  set both the HTTP and HTTPS values replacing the username/password and proxy address with your custom values:

npm config set proxy http://username:password@yourproxy.yourcompany.com:8080/
npm config set https-proxy http://username:password@yourproxy.yourcompany.com:8080/

To view the current proxy settings, or to check that your change worked, you can run “npm config get” (as opposed to “npm config set”) to read the settings.

“npm config get proxy”
“npm config get https-proxy”

Alternatively running only “npm config get” will show ALL NPM config settings.

Should you want to remove the npm setting you can do it like this:

“npm config rm proxy””
”npm config rm https-proxy”

For more information checkout the NPM documentation here:https://docs.npmjs.com/misc/config