Sometimes you want to display the continuous stream of data on a serial connection as a web page in a browser. How to achieve this? Very easy with a script...
Basically, it's just a matter of regularly creating a web page with the latest serial data. A web browser can then display the page on the local computer or over a network. A small program is therefore required that continuously converts the serial data into HTML files, for example. Isn't PHP also a programming language for Internet-related applications? Yes, and because of that, PHP makes it easy and elegant to do the job. But there are other ways, as we shall see.
Automatic refresh
HTML knows a meta tag that instructs the browser to load a web page with a certain delay:
If no URL is specified, this tag in the web page head instructs the browser to refresh the current page every ten seconds. Of course, the timeout can also be a different value. If your browser does not support this tag, it can be replaced with a short piece of JavaScript (download below). And if the page refreshes with new serial data every ten seconds, the browser will display that too.
Break the task in two!
The refresh tag can also be used in a PHP file instead of in an HTML file. The browser will react identically. This PHP file could also contain a script that reads the data from the serial port. This is where things get complicated because PHP doesn't natively support serial ports. And even if it were, every time the browser requests the latest version of the page, the script would have to open the serial port, get the current data, and close the port again. Data traversing the interface outside of this window would be lost. To top it all off, arduinoid systems could reset when opening the serial port, making the whole approach impossible. One solution to this is to split the process into two sections:
Section 1: A script to continuously read the serial port and update the data in a file imported from the PHP website (Listing 1).
Listing 1. A PHP script that reads the data from the serial port and writes it to the "data.txt" file.
// Linux $comPort = "/dev/ttyACM0";$comPort = "COM15";include "php_serial.class2.php";$serial = new phpSerial;$serial->deviceSet($comPort);/ / On Windows (10 only?) all mode settings must be done in one go.$cmd = "mode " . $comPort . " baud=115200 parity=n data=8 stop=1 to=off xon=off";$serial->_exec($cmd);$serial->deviceOpen();echo "Waiting for data...\ n";sleep(2); // Wait for Arduino to finish booting.$serial->serialflush();while(1){ $read = $serial->readPort(); if (strlen($read)!=0) { $fp = fopen("data.txt","w"); if ($fp!=false) { fwrite($fp,trim($read)); fclose($fp); } }}?>
Section 2: A browser that periodically refreshes the PHP web page to display the latest data (Listing 2, Figure 1).
Listing 2. This PHP webpage formats the contents of the data.txt file as a table.
";echo ""; // Page begin.echo "
",$page_title,"
"; // Head begin.echo "";echo "";echo "";echo "";echo "";echo ""; // Head end.echo "
"; // Body begin.echo "
",$page_title,"
"; // Page title.// Create a table from data file.$handle = fopen("data.txt","r");if ($handle!=NULL) { // Read one line from the file, then close it $data = fgets($handle); fclose($handle); // Synchronize to the data. if ($data[0]=='$') { // Remove whitespace. str_replace( ' ','',$data); // Split data into fields separated by ','. // Expected format: "$,id1,value1,id2,value2,CRLF" list($startchar,$id1,$value1 ,$id2,$value2,$newline) = explode(",",$data);// Create array from list.$numbers = array($id1=>$value1,$id2=>$value2); // Sort array in ascending key order. ksort($numbers); // Table begin. echo "
"; echo "
ID
value
"; foreach ($numbers as $x =" $x_value) { echo "
"; // Table row begin. echo "
", $x, "
"; // Table column 1. echo "
"; // Table column 2 begin. if ($x_value>=500) echo "
"; else echo "
"; echo $x_value; echo "
"; // Table column 2 end. echo "
"; // Table row end. } // Table end. echo "
"; }}echo ""; // Body end.echo ""; // Page end.?>
Web server required
This distributed approach solves the problem of opening and closing the port and the resulting data loss, but requires a script running in the background. If it is a PHP script, then the computer must also be able to run PHP scripts. A web server is also required to deliver the PHP web page to the browser. Without a web server, the browser would simply display the PHP code that makes up the page. The traditional way to achieve this is to install something called an AMP package. AMP stands for Apache MySQL PHP. Windows web servers are often preceded by a "W", Linux versions by an "L", i.e. WAMP or LAMP (but there are countless other web servers from mini to full-blown on the net).
Don't take PHP, ...
We tried PHP and managed to get the script working, but not without problems. Besides the difficulties in setting up the web server, the main problem was that PHP does not reliably open a serial port to receive data. There seems to be only one library for serial communication on the internet called PHP Serial, all others seem to be derived from it. As the author mentions on the GitHub page [2] "Windows: it seems to be working for some people, not working for some others." We clearly belonged to the second group. To get serial communication working with PHP, we first had to open the port with a serial terminal program like TeraTerm and immediately close it again. We have therefore abandoned the PHP method and opted for Python instead.
...take Python!
Python 3 with pySerial proved perfect on our Windows 10 test machine. We wrote a script to read data from the serial port and populate the web page with the data. Since PHP is no longer required, the Python script can also generate a pure HTML file (Listing 3, Illus. 2).
Listing 3. A Python script that reads data from the serial port and creates a corresponding HTML file. import serialimport timefile_name = "serial.html" # Once created, open this file in a browser.# Adapt serial port No. & baud rate to your system.serial_port = 'COM15'baud rate = 115200page_title = "Arduino on Python";def write_page(data_list): fo = open(file_name,"w+") # Start of HTML page. fo.write("") fo.write("
"+page_title+"
") # Page & Head begin. fo.write("") fo.write("") fo.write("") fo.write("") fo.write("") fo.write ("
"+page_title+"
") # Head end, body begin. # Table begin. fo.write("
") fo.write("
ID
value
") for i in range(0,len(data_list),2): fo.write("
") # Table row begin.fo.write("
"+data_list[i]+"
") # Table column 1. fo.write("
") # Table column 2 begin.fo.write("
") fo.write(data_list[i+1]) fo.write("
") # Table column 2 end. fo.write("
") # Table row end.fo.write("
") # Table end. fo.write("") # Body end. fo.write("") # Page end. # Done, close file. fo.close()s = serial.Serial(serial_port, baudrate) # Open serial port.s.dtr = 0 # Reset Arduino.s.dtr = 1print("Waiting for data...");time.sleep(2) # Wait for Arduino to finish booting.s.reset_input_buffer( ) # Delete any stale data.while 1: data_str = s.readline().decode() # Read data & convert bytes to string type. # Clean up input data. # Expected format: "$,id1,value1,id2 ,value2,...,CRLF" data_str = data_str.replace(' ','') # Remove whitespace. data_str = data_str.replace('\r','') # Remove return. data_str = data_str.replace(' \n','') # Remove new line.data_str += '123,65,1,999,cpv,236' # Add some more data print(data_str) # Split data in fields separated by ','.data_list = data_str. split(",") del data_list[0] # Remove '$' # Write HTML page. write_page(data_list)
All data formatting can also be done directly in Python. While the web page can still be served from a web server, a browser can also display and update the page locally. Therefore, a (W/L)AMP package is no longer required, which makes everything much easier.
Finally
In this article, we presented a method to display serial data in a web browser. The method is neither new nor necessarily the best. If you know another way - simpler, more elegant or whatever - please share it with us. And of course you don't need to write the script in Python, you can use any other programming language capable of handling serial communication and writing files. The advantage of Python (and pySerial) is that it runs on Windows, macOS and Linux (and more). The code developed for this article in the form of PHP and Python scripts and an Arduino sketch can be downloaded below.( 170111)
➔ Want to read more ElektorLabs articles? Join Elektor now!