For this project, we'll look at how to build a PetaLinux image for the MiniZed and create a webserver with a simple user interface accessible over a Wi-Fi connection to the MiniZed. For simple proof of concept, the main web page of the server will take a string input from the user and print it back to the screen.
To build the base hardware design for their boards, Avnet provides build scripts to create PetaLinux board support packages (BSPs). These scripts also build the base hardware design for the board when creating the BSP, so it's also a cheat to use as a starting point for a custom hardware design on the board as well. Using this guide from one of their blogs, I was able to build a hardware design and PetaLinux BSP for the MiniZed. I am working on Ubuntu 18.04.5 LTS using Vivado & PetaLinux version 2019.2. Some basic knowledge of Linux command line is assumed in this project (ie - changing directories, sourcing scripts, and setting project directories).
To start, create a singular folder to clone all of the Avnet repositories into as the scripts use relative pathways that only look in a single directory for all of their input files:
cd ~
mkdir -p ./git/avnet
cd ./git/avnet
Then clone the board definition file repository, hardware library repository, and PetaLinux repository from Avnet's GitHub:
git clone git://github.com/avnet/bdf.git
git clone git://github.com/avnet/hdl.git
git clone git://github.com/avnet/petalinux.git
Check out the master branch of the board definition files repository and the matching Vivado version you desire of the HDL and PetaLinux repositories (2019.2 in my case).
cd ./bdf
git checkout master
cd ../hdl
git checkout 2019.2
cd ../petalinux
git checkout 2019.2
For this project we are using the MiniZed booted from its eMMC flash memory, open the make_minized_emmc_enhanced_bsp script in /git/avnet/petalinux/scripts and modify the installations pathways for Vivado and PetaLinux to match that of your system.
I found that I had to remove the "../../" pathway directories for the folder path specifications in lines 72 - 77 to get the script to execute properly:
Save & close the script then run it from the command line:
/git/avnet/petalinux/scripts/make_minized_emmc_enhanced_bsp.sh
Once successful, you'll see the command line confirm the BSP is ready:
Navigate to the file, petalinuxbsp.conf, in /git/avnet/petalinux/projects/minized_emmc_enhanced_2019_2/project-spec/meta-user/conf/ and comment out the last two lines as they are no longer needed. They served to speed up the build of the BSP itself, but hinders building a project using the BSP.
Save & close petalinuxbsp.conf after making the edits.
Open a new terminal window and source the PetaLinux environment script:
source <PetaLinux installation path>/settings.sh
Create a new project using the BSP just created in a directory of your choice.
petalinux-create -t project -s ./git/avnet/petalinux/projects/minized_emmc_enhanced_2019_2.bsp
Then change directories into the newly created project:
cd ./minized_emmc_enhanced_2019_2/
For the Python web server using Flask, there are some extra packages that are needed from the base PetaLinux library that are not included in the default root filesystem configuration editor, namely the pip, threading, and multiprocessing packages (the rest are just ones I've found come in handy when adding additional webpages to the web server). To tell PetaLinux to include them in the next build, open user-rootfsconfig in the <PetaLinux project path>/project-spec/meta-user/conf directory and add the package names to be included:
# Extra system packages
CONFIG_gsl
CONFIG_cmake
# Python3
CONFIG_python3
CONFIG_python3-pip
CONFIG_python3-cffi
CONFIG_python3-numpy
CONFIG_python3-shell
CONFIG_python3-pyserial
CONFIG_python3-threading
CONFIG_python3-multiprocessing
Save & close user-rootfsconfig then open the root filesystem configuration editor:
petalinux-config -c rootfs
Navigate to the user packages tab where all of the packages just specified in user-rootfsconfig will appear. Scroll down and press the Y key when each is highlighted to specify for them to be included in the next build run.
Exit the root filesystem editor & save the configuration when prompted.
The MiniZed BSP includes the recipes for bringing up its onboard Wi-Fi chip. This is super handy as bringing up a wireless chip from scratch is quite the task. To streamline things, I like to modify the wpa_supplicant file in the recipe for the wireless driver to already have the SSID and password of the wireless network I will be connecting the MiniZed to.
The file wpa_supplicant.conf in <PetaLinux project directory>/project-spec/meta-user/recipes-bsp/minized-misc/files is the file referenced for the wireless SSID and password to the wireless network the MiniZed will be connected to. Replace with the desired SSID & password:
Finally, build the PetaLinux project:
petalinux-build
And create boot binary file for the MiniZed:
petalinux-package --boot --fsbl ./images/linux/zynq_fsbl.elf --u-boot ./images/linux/u-boot.elf --fpga ./images/linux/system.bit
The MiniZed ships with a default Linux image stored in the 8GB eMMC flash booted from the onboard QSPI flash memory. It is possible to update the MiniZed over a Wifi connection, so I will take advantage of that to upload the new custom Linux image just created from the BSP to the board.
Connect a micro-USB cable to the MiniZed's USB/JTAG/UART port and connect to the UART using your preferred serial terminal application (Putty, TeraTerm, etc.). The baud rate of the UART on the Zynq chip is 115200.
To get the MiniZed connected to a wireless network (since it still has the default image on it), edit the wpa_supplicant.conf file in the directory stored on the eMMC using vi:
root@MiniZed:~# vi /mnt/emmc/wpa_supplicant.conf
Replace the SSID & password fields with the SSID & password of the Wi-Fi network you want to connect the MiniZed to. Save & close the file then run the Wi-Fi setup script on the Minized:
root@MiniZed:~# wifi.sh
A successful connection will be indicated similarly as seen below with the output of the IP address assigned to the MiniZed.
The default Linux image the MiniZed comes shipped with, as well as the Linux image created by the BSP both have Dropbear SSH server installed on it. Using this, I will copy the necessary files for the new Linux image to the MiniZed via the SSH client.
The three files necessary are the new kernel image (<PetaLinux project directory>/images/linux/image.ub), the eMMC binary boot image (located in <PetaLinux project directory>/pre-built/linux/images/ labeled as BOOT_EMMC.bin), and the new wpa_supplicant.conf file (<PetaLinux project directory>/project-spec/meta-user/recipes-bsp/minized-misc/files/wpa_supplicant.conf).
It is very import that you use the eMMC boot binary generated by the BSP in the PetaLinux project. If a different version is used, it will result in a kernel panic at boot up.
Connect your PC to the same Wi-Fi network as the MiniZed. Use an FTP client to connect to the assigned IP address of the MiniZed (I'm using FileZilla).
Select the the eMMC boot binary file, the new wpa_supplicant.conf, and the new Linux kernel and upload them to the eMMC of the MiniZed (the /run/media/mmcblk1p1 directory in the MiniZed's filesystem is the eMMC flash).
Verify the file transfer:
root@MiniZed:/usr/local/bin# cd /run/media/mmcblk1p1/
root@MiniZed:/run/media/mmcblk1p1# ls -l
Use the flashcp command to flash the eMMC boot binary the into MiniZed's QSPI flash then reboot the MiniZed:
root@MiniZed:/run/media/mmcblk1p1# cd ~
root@MiniZed:~# flashcp /run/media/mmcblk1p1/boot_eMMC.bin /dev/mtd0
root@MiniZed:~# reboot
Once rebooted, run the wifi script again to connect to Wi-Fi:
root@minized-emmc-enhanced-2019-2:~# wifi.sh
Then change directories into the eMMC memory, as anything not saved there will be deleted at the next power cycle:
root@minized-emmc-enhanced-2019-2:~# cd /run/media/mmcblk1p1/
This will be a simple flask web server written in Python using GET and POST methods to handle requests between the MiniZed and the user client.
To start, install flask and requests Python packages (specifying pypi.org and files.pythonhosted.org as trusted hosts):
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1# pip3 install flask --trusted-host pypi.org --trusted-host files.pythonhosted.org
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1# pip3 install requests --trusted-host pypi.org --trusted-host files.pythonhosted.org
Create a directory for the project files for the web server (yes, the name is a hint to a future project):
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1# mkdir -p gameboard
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1# cd ./gameboard
Create a directory titled 'templates' for the basic web server templates to define the configuration settings for the web pages and and a directory titled 'static' for the necessary static content such as the CSS and JavaScript files.
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1/gameboard# mkdir -p templates
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1/gameboard# mkdir -p static
For the image files the web page will use, create a directory titled 'images' :
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1/gameboard# mkdir -p ./static/images
I then used the FTP client to transfer my Knitronics logo image file and the Xilinx favicon image file to the directory.
For the CSS, JavaScript files, and fonts, I stole them from the Ultra96 web server project files and again transferred them to the MiniZed using the FTP client:
There are two main templates needed for the webpages: a default base template that all pages will be based from, and the home page template that uses the default template with the home pages specific elements (the home page will be the only page for this project, but every addition webpage added will need its own specific template).
Create the Home and Default directories in the templates directory:
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1/gameboard# mkdir -p ./templates/Home
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1/gameboard# mkdir -p ./templates/Default
Create the home page HTML template:
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1/gameboard# vi ./templates/Home/home.html
home.html code (note it is an extension of the default page template as stated previously):
{% extends "Default/default.html" %}
{% block content %}
<div class="page-header">
<h1 class="display-4"><b>{% block title %}Input Phrase{% endblock %}</b></h1>
</div>
<form action="/home.html" method="POST" enctype="multipart/form-data">
<label for="phrase">Input Phrase:</label>
<input type="text" id="phrase" name="phrase"><br><br>
<input type="submit" value="Submit">
</form>
<br><br>
<h2>Input phrase: {{ output }}</h2>
{% endblock %}
Create the default HTML template:
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1/gameboard# vi ./templates/Default/default.html
default.html code (also stolen from the Ultra96 web server and edited to use my Knitronics logo instead of the Ultra96 logo):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}{% endblock %}</title>
<link href="{{ url_for('static', filename='css/bootstrap.min.css')}}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/bootstrap-theme.min.css')}}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/theme.css')}}" rel="stylesheet">
<script src="{{ url_for('static', filename='js/jquery-3.2.1.js')}}"></script>
<script src="{{ url_for('static', filename='js/bootstrap.js')}}"></script>
<link rel="shortcut icon" href="{{ url_for('static', filename='images/knitronics_full-color.png')}}">
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="/" class="pull-left"><img src="static/images/knitronics_full-color.png" class="img-responsive" width="250" height="50"/></a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/home.html">Home</a></li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="row row-offcanvas row-offcanvas-right">
{% block content %}
{% endblock %}
<hr>
<footer>
<p>Knitronics</p>
</footer>
</div>
</div>
<div id="myModal" class="img-modal">
<span class="close">×</span>
<img class="img-modal-content" id="modalContainer">
</div>
<script>
var modal = document.getElementById('myModal');
var modalImg = document.getElementById("modalContainer");
var span = document.getElementsByClassName("close")[0];
function showImageModal(params){
modal.style.display = "block";
modalImg.src = params.src;
}
span.onclick = function(){
modal.style.display = "none";
}
function submitFormAsync(request, data){
$.ajax({
type: request.method,
url: request.action,
data: data,
success: function(response) {}
});
return false;
}
</script>
</body>
</html>
Create the webserver python script:
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1/gameboard# vi webserver.py
Web server code:
# MiniZed webserver script
import os
import re
import sys
import glob
import time
import uuid
import crypt
import signal
import random
import os.path
import subprocess
import multiprocessing
from flask import Flask, render_template, request
from multiprocessing import Pipe
CUR_DIRECTORY = os.path.split(os.path.abspath(__file__))[0]
ALERT = -1
app = Flask(__name__)
app.debug = True
global timer_status, timeout, elapsed_time, pconn, cconn
timeout = 15
elapsed_time = 5
timer_status = "timer_disabled"
pconn, cconn = Pipe()
print("Hello MiniZed!!!")
print(CUR_DIRECTORY)
@app.route('/')
@app.route('/home.html', methods=['GET','POST'])
def home():
if request.method == "POST":
phrase = request.form.get("phrase", None)
if phrase!=None:
proc = subprocess.Popen('python3 /run/media/mmcblk1p1/gameboard/spell_phrase.py '+phrase+'' ,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=True)
output,err = proc.communicate()
return render_template("Home/home.html",output=output,phrase=phrase)
phrase = None
return render_template("Home/home.html")
if __name__=='__main__':
app.run(host='0.0.0.0', port=80, threaded=True)
The main function of the home page right now is taking in an input string from the user on a POST request, passing it to an external application Python script, then returning the output from that script to the home page.
Create the application specific python script:
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1/gameboard# vi spell_phrase.py
Application specific code:
import os
import re
import sys
import time
import requests
import subprocess
#print("Hello Gameboard")
def spell_phrase(phrase):
if(phrase!=None):
print(phrase)
else:
phrase = "noPhrase"
return phrase
def main(arg):
phraseOut = spell_phrase(arg)
#print(phraseOut)
if __name__ == "__main__":
main(sys.argv[1])
For demonstration purposes, alls the application script is doing is taking in the string from the home page then echoing it back.
Run the script to start the webserver:
root@minized-emmc-enhanced-2019-2:/run/media/mmcblk1p1/gameboard# python3 webserver.py
Connect to the web server from your PC using the assigned IP address of the MiniZed in a browser window. Since the debugger is enabled in flask, you'll see the requests between the MiniZed and PC in the terminal output of the MiniZed.
Once connected, testing the phrase input application script:
You can watch the flask debug output in the MiniZed's terminal to get a better understanding of POST and GET methods.
To avoid corrupting the files in memory, always be sure to safely shut the MiniZed down:
root@minized-emmc-enhanced-2019-2:~# shutdown -h now
I have added all of the project files generated to a compressed file attached below for reference.
Comments