Python - RS232 Port - Local Webpage

Hello Folks,
I think this is the first time I have ever posted to a forum. So if I am doing something wrong, please let me know. I haven't written code in over 15 years and now I have a IoT project and no resources to complete. I have a bunch of legacy equipment that is RS232 based, I need to pull information from the unit and then do two things. 1. Use that data and display it on a locally hosted web page as close to real time as possible and 2) Send that data to a database server out on the cloud every hour or so.

Heres how I proceeded with the setup:

  1. Arduino Yun
    -- I added an SD card to the system per the instructions
  2. FTDI USB to RS232 cable to legacy unit
  3. I really have no need for the Arduino side except for maybe some status indicators, maybe console for debugging, who knows

I wrote a python program that polls the legacy equipment and was using puts into the key/value datastore.
I then I wrote a index.html file which I placed in the /mnt/sda1/project/www folder
I used bootstrap.js for the formatting of the page and to provide a responsive web page which will allow the data formatting to change based on the device.

So the script on the arduino side, basically just starts a project.py script and lets it run asynchronously. I was eventually going to place this in the startup process on the linino side. Currently I just start it from a ssh CLI. The project.py opens the USB port, queries the device, and then puts the data in the datastore. I like this because at the end of the process I can do a /data/get and I have a complete JSON variable. I use this same variable to pass into my local web page an parse with json/AJAX and I also post to my data server out on the web if it is time to do so.

Everything was OK until I started to reach a certain number of puts/gets

  1. The puts on the python very slow (I believe this is due to the opening/closing of put command for every key
    -- I have tried everything to speed this up (JSONTCPclient, bridgeclient, etc)
  2. With the large JSON packet the web site would receive just pieces and then eventually stop taking in new values
    -- Don't know why

I believe I can use the bridge side of things just to handle the couple I/O pieces that I may need latter on.

So, I was hoping someone could provide me a little direction on how to store item in memory that can be used for both the python program and the web page.

Any help is appreciated, I am attaching chunklets of code to show what I have tried so far.

This is the free running python script

#!/usr/bin/python
import time
import serial
import crcmod
import binascii
import sys

#bring in the python bridge code
sys.path.insert(0,'/usr/lib/python2.7/bridge/')
from bridgeclient import BridgeClient as bridgeclient

# key:value dictionaries to hold all data for web page and Arduino side
# Bridge is required to be running for these to work correctly
dictText={} 	#Human Readable Key:Value
dictTextSlow={} 
dictHex={} 	#Hex String key is the command, value is the response with ID 

---- bunch of boring stuff --- 

---- Stuff I tried for speeding up the puts -----
#--------------------------------------------------------------------------------
# Function: keyValuePut
# Dictionary: dictText
# this was very fast, issue was that it would cause issues with the local www
# //arduino.locaa/data/get would have incomplete data ALOT
#--------------------------------------------------------------------------------
#from tcp import TCPJSONClient
#json = TCPJSONClient('127.0.0.1',5700)

#def jsonValuePut(dictName):
  #json = TCPJSONClient('127.0.0.1',5700)
  #for key,value in dictName.iteritems():
    #json.send({'command':'put','key':key, 'value':value})
    #time.sleep(10)
  #json.close()

client = bridgeclient()
client.timeout = 30
#--------------------------------------------------------------------------------
#Function: keyValuePut
#Dictionary: dictText
#Very Slow - opens and close port for each key:value put
#--------------------------------------------------------------------------------
def keyValuePut(dictName):
  for key,value in dictName.iteritems():
    #print key,value
    client.put(key,value)

------------------- This is the usb/rs232 port open function ----------------------
def cfgPort():
  global usbSer
  usbSer = serial.Serial( port='/dev/ttyUSB0', baudrate=38400, parity=serial.PARITY_NONE,
      stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=1 )
  if usbSer.isOpen():
    print "Port already open"
    return True
  else:
    print "Opening Port:"

----------- Main loop ------------------------------------
the getXXX function parse the received data and put it into a dict in key:value format
the 

while 1:
  print '-------- Configuration -----------------'
  counter = 0
      
  #getTotals()
  #time.sleep(.250)
  
  #getErrorLog()
    
  #print '--- Writing Slow Text Dictionary ----'
  #keyValuePut(dictTextSlow)
  #jsonValuePut(dictTextSlow)
  #clientValuePut(dictTextSlow)
      
  while counter < 30:
    print '--------- Scanned data ---- Counter: %s -------------' % counter
    getTimeDate()
    time.sleep(.250)
       
    getSwVersion()
    time.sleep(.250)
   
    getConfig()
    time.sleep(.250)
    
    getStat()
    time.sleep(.250)

    getStates()
    time.sleep(.250)
        
  
    print '--- Writing Fast Text Dictionary ----'
    #keyValuePut(dictHex)
    #print dictHex
    keyValuePut(dictText)
    #jsonValuePut(dictText)
    #clientValuePut(dictText)
    print dictText
    counter +=1

So that is the continuously running script on the linino side, this is the js that gets the data and updates the index.html with received json package - It is based off of the keystore_manager_example on the Yun itself

/***************************************************************************
  This is what runs on a page refresh/load
  On Clicks are handled here also
***************************************************************************/
$(function() {
  $("#storage").html(please_wait_message);
  storage_retrieve_all(repaint_storage);

function storage_retrieve_all(callback) {
  var ajax_call_options = {
  url: "/data/get",
  timeout: 10000,
  success: callback,
  error: error_handler
  };
  add_basic_auth_headers_if_password(ajax_call_options, $("#password").val());
  $.ajax(ajax_call_options)
}

function repaint_storage(storage) {
  $("#storage").empty();
  if (storage.value.length === 0) {
    $("#storage").html("Storage is empty!");
    return;
  }
  
  
  NumA = storage.value["x"];
  NumB = storage.value["y"];
  /*
  //Use this to print out all the keys and their values
  for (var key in storage.value) {
    html += "<li>" + key + ":" + storage.value[key] + "</li>";
  }
  //document.getElementById(put id here).innerHTML = storage.value[put key here];
  document.getElementById("vince1").innerHTML = html;
  */
  
  document.getElementById("a").innerHTML = storage.value["A"];
  document.getElementById("b").innerHTML = storage.value["B"];
  document.getElementById("c").innerHTML = storage.value["C"];
  document.getElementById("d").innerHTML = storage.value["D"];
  document.getElementById("e").innerHTML = storage.value["E"];
  document.getElementById("f").innerHTML = storage.value["F"];
  document.getElementById("g").innerHTML = storage.value["G"];
  document.getElementById("h").innerHTML = storage.value["H"];
  document.getElementById("i").innerHTML = storage.value["I"];

There must be an easier and more reliable way to do this!! I just can't seem to figure it out.

defiantVatic:
::::SNIP::::

So, I was hoping someone could provide me a little direction on how to store item in memory that can be used for both the python program and the web page.

Any help is appreciated, I am attaching chunklets of code to show what I have tried so far.

::::SNIP::::

There must be an easier and more reliable way to do this!! I just can't seem to figure it out.

@defiantVatic,
hmmmm.... Well, this depends on how much work you want to do.

First, you probably don't want to use bridge.py that way. That entire software framework is intended for use with the Arduino part. As the name implies, it "bridges" the arduino (ATmega32U4) and the "linux" (Atheros AR9331) . You're better off buying an SD (microSD) storage chip and put your data there, then ship it as you need to the Internet.

The software you'll need:

  1. a serial port data collector
  2. a data collector to storage module
  3. a storage to Internet module
  4. a REST-style API for the webpage (CGI python)

After that you could use the bridge library from the Arduino side to write to the SD - using the FileIO class. Likely, 5-10 days work(since you don't that yet ; 2-3 days from scratch if you knew it), but once you "get it" - life will be better. :wink:

FWIW: I'm busy for the next two days, but I'm sure someone else will chime in.

Jesse

While http://arduino.local/data/get has some appeal, I agree it might not be the best solution here.

As I see it, the gist of the problem is that the data that is read over the serial port must be stored in a way that is accessible to two processes: the one reading the data from the device and buffering it, and the one reading the buffer and sending it to the web page. I'm not a Linux guru, so I can't give details, but I can throw out a few ideas and see if it triggers inspiration in anyone.

This seems like an ideal application for shared memory. I have no idea how to do it in Python, but it may be possible.

I know Linux supports pipes where one process writes to the pipe, and the other reads. But that doesn't sound like the ideal solution, since I don't think you necessarily want to get every byte to the web page, just the last sample is wanted. Perhaps two pipes could be used: the device comm process, which stores the latest set of samples in local variables, periodically looks at an incoming request pipe to see if there is anything there: if so it discards whatever it reads, and writes the current set of samples to an outgoing response pipe. A web CGI script uses the pipes in the opposite direction: it first reads and discards everything in the incoming response pipe (in case there is old data in it) then sends a request in the outgoing request pipe. It then waits for and receives the latest sample from the response pipe, formats it, and returns the web response. A bit convoluted, but ensures only the latest sample gets written to the pipe.

A file would be an easy way to communicate. If the latest data were written to a file in /mnt/sda1/www, and constantly overwritten, it could be accessed directly by the web server process. But writing to the SD card is relatively slow, and flash memory wears out over time with repeated writes. The /tmp folder is actually in RAM, so that would be a fast way to read and write the file. But I don't think the web server can access that folder directly: so that means either writing a CGI script to read and return the data, or modifying the web server configuration.

A potential issue with any kind of shared resource (memory, file, whatever) is coherency: you don't want to read the data store while it is being updated, and don't want to update it while it's being read. Partially updated data could result, which could give incorrect values. So some sort of lacking/arbitration method is needed. This should not be an issue with the two pipe method.

It is a given that there is a continually running process to read the device. And of course this process can keep the data in local variables. Is there a way for another process (web CGI script) to make a call into this process to read those variables? Perhaps some sort of socket (network on localhost?) This could work much like the two pipe method but bi-directionally over the same connection. The pipes probably are more efficient?

There has to be a clean way of doing this, it can't be the first time this problem has ever come up. Hopefully someone will have the solution, or perhaps these rumblings may trigger an idea in someone?

Thanks for the input folks. I stumbled upon CGI after I wrote the post. I have been looking into spacebrew also. Not quite sure what to make of it at this time. I believe the issue I may be having is the related to coherency right now :-[ ,

What do you think of using the mailbox? Post a message for the local web page to read? I will keep at it, once I find a solution I will post, again any help is appreciated

I'd stay away from the mailbox. If put gave you problems, I think mailbox will give you more. I believe the use case for mailbox is for the Linux side (or the http://arduino.local/mailbox/... API) to put messages into a queue that can be read by the sketch. I don't think that Linux or the web API has the ability to read from the mailbox, only the sketch. And, like the simple pipe example, updates will tend to accumulate in the queue if the device reader script puts data in faster than the data is pulled out - you will fill the queue with lots of old data.

Using RS232 to USB convertor (usb-serial-pl2303) and Python

http://forum.arduino.cc/index.php?topic=278719.msg1959911#msg1959911

Insert data into sqlite3

http://forum.arduino.cc/index.php?topic=261296.msg1848376#msg1848376

Using google chart and php to display chart

http://forum.arduino.cc/index.php?topic=233683.msg1683791#msg1683791