Weigh Scale MQTT
Contents
- 1 Introduction
- 2 Requirements
- 3 Setup
- 4 Technical Details
- 4.1 Initialize the Serial Ports via PySerial
- 4.2 Read Scale data
- 4.3 Processing the new weight readings and taking actions
- 4.4 Sending Data to Google Analytics
- 4.5 Sending Data to 2lemetry Cloud Platform using MQTT
- 4.6 Module Import and Initialization
- 4.7 Program Initialization
- 4.8 Web Server Initialization and Handlers
Introduction
This demonstration sample shows how easy it is to add local processing to the IoT network edge and communicate with cloud based IoT and Analytics platforms. Here we see the Lantronix PremierWave family of Intelligent Gateways connect to an end device via a serial port like RS-232/485, or Ethernet, intelligently extract useful data, and send it to 2lemetry Cloud Platform and Google Analytics. Standard and scalable M2M/IoT transport protocols such as MQTT and HTTP are easy to integrate within a custom application while relying on the secure remote access and robust device management features of the rugged Cellular M2M Gateways such as Lantronix PremierWave XC HSPA+.
Requirements
- Python module PySerial - built-in module supported in Python for Lantronix products
- Tornado webserver package
- Digital scale with RS232 serial port (Homedics 349KLX)
Any other serial device could be used and its protocol implemented in Python instead - Internet Connection via cellular or Ethernet
Setup
The demonstration setup consists of the digital scale with RS232 connected to one of the RS232 ports on PremierWave XC HSPA+. The Python script performs the following actions:
- Values from digital scale are read over the RS232 port
- Send SMS to user’s cell phone with the weight read from scale
- Digital scale readings are posted to 2lemetry cloud via MQTT protocol
- Digital scale readings are posted to Google Analytics via HTTP
- Local access to the web server via the browser provides scale read-out
Technical Details
Initialize the Serial Ports via PySerial
The program uses the PySerial module that is built-in the Python support on Lantronix devices. For more details on the module refer to the ]]//Lantronix_Python_Programmers_Guide | Programmer's Guide]].
class ser349klx: # setup the serial port. Pass the device as '/dev/ttyS1' or '/dev/ttyS2' for # serial port 1 and 2 (respectively) in PremierWave EN or XC HSPA+ def __init__(self, device, weight, c21, ga): while True: try: serstat = True ser = serial.Serial(device,2400, interCharTimeout=0.2, timeout=1) except Exception: serstat = False if serstat: break self.ser = ser self.weight = weight self.c21 = c21 self.ga = ga
Read Scale data
The scale constantly sends the current weight via the RS232 port, with each value separate by a carriage return
def receive_line(self): buffer = while True: buffer = buffer + self.ser.read(self.ser.inWaiting()) if '\r' in buffer: lines = buffer.split('\r') return lines[-2]
Processing the new weight readings and taking actions
# This runs a continuous loop listening for lines coming from the # serial port and processing them. def getData(self): count = 0 prev = 0.0 #print self.ser.interCharTimeout while True: time.sleep(0.1) try: val = self.receive_line() weight.value=float(val[-5:])*0.166 if (prev == weight.value): count += 1 if (count == 10) and (str(prev) != '0.0'): self.ga.send("{:.2f}".format(prev)) if supportMqtt: self.c2l.send("{:.2f}".format(prev)) else: count = 0 prev = weight.value except Exception: pass
Sending Data to Google Analytics
Since the Google Analytics Measurement Protocol uses standard HTTP requests to send data from devices other than web browsers, the ga.send method is easily implemented using the Python urllib and urllib2 modules.
class gaConnect: def __init__(self, tracking, mac): self.tracking = tracking self.mac = mac def send(self, data): values = { 'v' : '1', 'tid' : self.tracking, 'cid' : self.mac, 't' : 'event', 'ec' : 'scale', 'ea' : 'weight', 'el' : data } res = urllib2.urlopen(urllib2.Request("http://www.google-analytics.com/collect", urllib.urlencode(values)))
Sending Data to 2lemetry Cloud Platform using MQTT
The sampled data is sent to the 2lemetry cloud platform using the MQTT protocol. MQTT client is implemented using the imported Mosquitto package and its functions are used for communicating with 2lemetry via MQTT.
class connection2lemetry: def __init__(self, dev): self.client = mosquitto.Mosquitto('things/'+dev.mac) self.client.username_pw_set("USERNAME", password="PASSWORD") #self.client.connect("q.m2m.io") #self.client.loop() self.id = dev.mac def send(self, value): self.client.connect("q.m2m.io") self.client.loop() self.client.publish('30025f33189599eed42d868bce9670dd/things/'+self.id,'{"report":{"test":{"weight":"'+value+'"}}}',1) self.client.loop() self.client.disconnect() def disconnect(self): self.client.disconnect()
Module Import and Initialization
The following section of the code imports the required modules and checks for the installation of the tornado (Web Server) and mosquitto (MQTT client) modules.
import logging logging.basicConfig(level=logging.WARNING, filename='ew2014debug.log') import time import serial import commands import os from multiprocessing import Process, Value import threading import urllib2 import urllib try: import tornado.ioloop import tornado.web except Exception, ex: logging.exception('Make sure Tornado and backports are correctly installed') try: import mosquitto supportMqtt = True except: supportMqtt = False logging.exception('Make sure Mosquitto module is installed')
Program Initialization
Program initialization reads the device serial number (MAC address), initializes the connection to the Google Analytics and 2lemetry cloud platform with the appropriate credentials (replaced here with obscured credentials). The tornado web server is started and the program enters a loop where it processes the serial data from the weighing scale and processes it for transmission to the browser (locally served up web pages), Google Analytics and 2lemetry cloud platform.
if __name__ == '__main__': # time.sleep(30) #os.system('route add default gw 172.19.0.1 eth0') os.system('/etc/init.d/S19-ltrx-network-common') dev = pwDevice() print dev.mac weight = Value('d',0.0) ga = gaConnect("UA-YYYY", dev.mac) c2l = None if supportMqtt: try: c2l = connection2lemetry(dev) except Exception, ex: logging.exception('Failed to connect to 2lemetry') p = Process(target=startServer, args=(8888,weight,)) p.start() time.sleep(1) s = ser349klx('/dev/ttyS2', weight, c2l, ga) p2 = Process(target=s.getData) p2.start() p.join() p2.join()
Web Server Initialization and Handlers
The web server is initialized with the handlers for the supporting URLs and starts processing HTTP requests. The implementation of the various handlers is also shown below.
class MainHandler(tornado.web.RequestHandler): def get(self): self.render("ew2014demo.html") class weightHandler(tornado.web.RequestHandler): def initialize(self, weight): self.weight = weight def get(self): self.write("{:.2f}".format(self.weight.value)) class cellHandler(tornado.web.RequestHandler): def initialize(self, weight): self.weight = weight def post(self): num = self.get_argument('number',) print num if num: os.system('ctest sms-send ' + num + ' ' + str(self.weight.value)) print num self.render("ew2014demo.html")
def startServer(port,weight): settings = { "static_path": os.path.join(os.path.dirname(__file__), "static"), } application = tornado.web.Application([ (r'/', MainHandler), (r'/weight', weightHandler, dict(weight=weight,)), (r'/submit_cell', cellHandler, dict(weight=weight,)), (r'/static/(.*)', tornado.web.StaticFileHandler, dict(path=settings['static_path'])), ]) application.listen(port) tornado.ioloop.IOLoop.instance().start() print 'Started web server'