gillettos
Posts: 20
Joined: Fri Sep 21, 2012 8:20 am

Analog I/P's and a GUI

Mon May 18, 2015 6:07 pm

Hi all,

After a bit of advice / guidance.......

I've been playing with my Pi now for a while and just come across something which has made me think I should be doing things differently.......

In a nut shell......I'm wanting to display some analog I/P devices on a GUI. To do this I have an MCP3008 A>D convertor and atm a basic potentiometer so I can play and test my code, re-scale, etc etc.

The issue.......

I'm using WebIOPI (python & javascript), to write my programme and finding it a bit tricky as the python coding side is new to me, and it seems very lengthy. Then there's the html code....... The two as you all no work hand in hand however 1 slight mistake and there's no web page displayed......

Anyways...... I've now stumbled across this..... viewtopic.php?f=44&t=46881&sid=daa159a9 ... &start=125. Which basically uses an Arduino Nano which interfaces through the serial point.

I like the idea of this as it's meant to be more accurate, and the coding can be done in VB...... :0)

The question is really, can I still use a html file (with say... Justgauge dials), to interface with the pi??? The whole point of the project is have a nice HMI / GUI displaying water levels, temperatures, voltage and battery levels etc etc.

Would be really really great to have some kind of PLC package for all this making a HMI really easy!!! But as far as I can see these don't exist. This nano uses nanpy, github........? to which I have no experience off

I've attached my two files.... but their nothing impressive atm as I'm just getting into this...... edit..... If the site allowed :0(

Sorry for going on, but any help / advice will be massively appreciated.

Cheers,

Rich

danjperron
Posts: 3440
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Analog I/P's and a GUI

Tue May 19, 2015 1:35 am


gillettos
Posts: 20
Joined: Fri Sep 21, 2012 8:20 am

Re: Analog I/P's and a GUI

Tue May 19, 2015 7:52 am

Okay cheers,

Would you say this is easier or the same as using the arduino nano, just got a few doubts about the MCP3008....?

danjperron
Posts: 3440
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Analog I/P's and a GUI

Tue May 19, 2015 11:13 am

Depends what you want to do.

The Arduino mini uses less power than the Pi and via a serial or a RF link it will be easier to bring the A/D near the source of the signal.

On the other hand if everything is close to the Pi then the Arduino is not needed.

I'm doing RF communication using the arduino mini with RF24. Now the Arduino mini has a big advantage since my sensor only use type 357 or AAA battery and no wire at all.

viewtopic.php?p=720838#p720838
viewtopic.php?p=736234#p736234

User avatar
RogerW
Posts: 286
Joined: Sat Dec 20, 2014 12:15 pm
Location: London UK

Re: Analog I/P's and a GUI

Tue May 19, 2015 11:49 am

These bits would enable you to implement a GUI in python. This file implements an ADC class which would allow you to read the ADC. It uses joan's pigpio but could be modified to use spidev.

Code: Select all

# Objects implemented using joan's pigpio
# written by Roger Woollett

import pigpio as pg
import socket as soc

class Pio():
	# to override the default host name (ip address) and port
	# assign to Pio.host and/or Pio.port in the calling file
	
	# static data
	_started = False
	_refcount = 0
	
	def __init__(self):
		Pio._refcount += 1
		self._closed = False
		if not Pio._started:
			
			try:
				Pio.host
			except AttributeError:
				Pio.host = 'localhost'
				
			try:
				Pio.port
			except AttributeError:
				Pio.port = 8888
				
			# now see if daemon is running
			try:
				socket = soc.create_connection((Pio.host,Pio.port))
			except:
				dok = False
			else:
				socket.close()
				dok = True
			
			if not dok:
				self._closed = True
				raise RuntimeError('Cannot connect to pigpio daemon')
					
			Pio.pi = pg.pi(Pio.host,Pio.port)
			Pio._started = True
			
	def close(self):
		Pio._refcount -= 1
		self._closed = True

		if Pio._refcount == 0:
			Pio.pi.stop()
			Pio._started = False
			
	def __del__(self):
		if not self._closed:
			raise RuntimeError('Close not called in Pio object')

class ADC(Pio):
	# class to access a MCP3008 A to D converter
	# pins used are:
	# Name board(gpio) - chip pin
	# MOSI	19(GPIO10)	11 - data in
	# MISO	21(GPIO9)	12 - data out
	# SCLK	23(GPIO11)	13 - clock
	# CE0	24(GPIO8)	10 - chip select
	# CE1	26(GPIO7)	10 - chip select on second chip
	# There is a second set of pins used on B+ and Pi2
	# This auxilliary port is not supported as yet
	def __init__(self,chip):
		Pio.__init__(self)
		#chip should be 0 (chip connected to CE0) or 1 (CE1)
		if chip < 0 or chip > 1:
			Pio.close(self)
			raise RuntimeError('chip must be 0 (CE0) or 1 (CE1)')
			
		self.handle = Pio.pi.spi_open(chip,50000,0)
		
	def close(self):
		Pio.pi.spi_close(self.handle)
		Pio.close(self)
		
	def read(self,channel):
		# channel corresponds to pins 1 (ch0) to 8 (ch7)
		# we have to send three bytes
		# byte 0 has 7 zeros and a start bit
		# byte 1 has the top bit set to indicate
		# single rather than differential operation
		# the next three bits contain the channel
		# the bottom four bits are zero
		# byte 2 contains zeros (don't care)
		# 3 bytes are returned
		# byte 0 is ignored
		# byte 1 contains the high 2 bits
		# byte 2 contains the low 8 bits
		if channel < 0 or channel > 7:
			raise RuntimeError('channel must be 0 - 7')
			
		(count,data) = Pio.pi.spi_xfer(self.handle,[1,(8 + channel) << 4,0])
		if count > 0:
			return (data[1] << 8) + data[2]
		else:
			return 0
This object implements a meter which you can us in a tkinter based GUI. I use python3 if you want to use python 2 use Tkinter and ttk

Code: Select all

# Implement a meter object
# written by Roger Woollett

import tkinter as tk
import tkinter.font as tkf
import math

# class to show a gauge or panel meter
class Meter(tk.Canvas):
    def __init__(self,master,*args,**kwargs):
        super(Meter,self).__init__(master,*args,**kwargs)
        
        self.layoutparams()
        self.graphics()
        self.createhand()
        
        self.setrange()
        
    def layoutparams(self):
        # set parameters that control the layout
        height = int(self['height'])
        width = int(self['width'])
        
        # find a square that fits in the window
        if(height*2 > width):
            side = width
        else:
            side = height*2
        
        # set axis for hand
        self.centrex = side/2
        self.centrey = side/2
        
        # standard with of lines
        self.linewidth = 2
        
        # outer radius for dial
        self.radius = int(0.40*float(side))
        
        # set width of bezel
        self.bezel = self.radius/15
        self.bezelcolour1 = '#c0c0c0'
        self.bezelcolour2 = '#808080'
    
        # set lengths of ticks and hand
        self.majortick = self.radius/8
        self.minortick = self.majortick/2
        self.handlen = self.radius - self.majortick - self.bezel - 1
        self.blobrad = self.handlen/6
             
    def graphics(self):
        # create the static components
        self.create_oval(self.centrex-self.radius
        ,self.centrey-self.radius
        ,self.centrex+self.radius
        ,self.centrey+self.radius
        ,width = self.bezel
        ,outline = self.bezelcolour2)
        
        self.create_oval(self.centrex-self.radius - self.bezel
        ,self.centrey-self.radius - self.bezel
        ,self.centrex+self.radius + self.bezel
        ,self.centrey+self.radius + self.bezel
        ,width = self.bezel
        ,outline = self.bezelcolour1)
        
        for deg in range(-60,241,6):
            self.createtick(deg,self.minortick)
        for deg in range(-60,241,30):
            self.createtick(deg,self.majortick)
        
    def createhand(self):
        # create text display
        self.textid = self.create_text(self.centrex
        ,self.centrey - 3*self.blobrad
        ,fill = 'red'
        ,font = tkf.Font(size = -int(2*self.majortick)))
        
        
        # create moving and changeable bits
        self.handid = self.create_line(self.centrex,self.centrey
        ,self.centrex - self.handlen,self.centrey
        ,width = 2*self.linewidth
        ,fill = 'red')
        
        self.blobid = self.create_oval(self.centrex - self.blobrad
        ,self.centrey - self.blobrad
        ,self.centrex + self.blobrad
        ,self.centrey + self.blobrad
        ,outline = 'black', fill = 'black')
        
    def createtick(self,angle,length):
        # helper function to create one tick
        rad = math.radians(angle)
        cos = math.cos(rad)
        sin = math.sin(rad)
        radius = self.radius - self.bezel
        self.create_line(self.centrex - radius*cos
        ,self.centrey - radius*sin
        ,self.centrex - (radius - length)*cos
        ,self.centrey - (radius - length)*sin
        ,width = self.linewidth)
        
    def setrange(self,start = 0, end=100):
        self.start = start
        self.range = end - start
        
    def set(self,value):
        # call this to set the hand
        # convert value to range 0,100
        deg = 300*(value - self.start)/self.range - 240
        
        self.itemconfigure(self.textid,text = str(value))
        rad = math.radians(deg)
        # reposition hand
        self.coords(self.handid,self.centrex,self.centrey
        ,self.centrex+self.handlen*math.cos(rad), self.centrey+self.handlen*math.sin(rad))
        
    def blob(self,colour):
        # call this to change the colour of the blob
        self.itemconfigure(self.blobid,fill = colour,outline = colour)
Finally a test program. You need to install pigpio and start the daemon and put all three files in the same directory for them to work.

Code: Select all

# Test program to try out ADC object
# written by Roger Woollett

import tkinter as tk
import tkinter.ttk as ttk

import meter as m
import pigpio as pg

from pio import Pio,ADC

class Mainframe(ttk.Frame):
	def __init__(self,master,*args,**kwargs):
		ttk.Frame.__init__(self,master,*args,**kwargs)
		
		self.bind('<Destroy>',self.onDestroy)
		
		self.mv = m.Meter(self,height = 200,width = 200)
		self.mv.setrange(0,3300)
		self.mv.set(0)
		self.mv.grid(row = 0, column = 0)
		
		self.adc = ADC(0)
		ttk.Button(self,text = 'Read',width = 25,command = self.readADC).grid(row = 1,column = 0)
	
		ttk.Button(self,text = 'Quit',width = 25,command = master.destroy).grid(row = 2,column = 0)
		
	def readADC(self):
		milli = 3300*self.adc.read(7)/1024
		self.mv.set(int(milli))
		
	def onDestroy(self,var):
		self.adc.close()
			
class App(tk.Tk):
	def __init__(self):
		tk.Tk.__init__(self)
	  
		# set the title bar text
		self.title('PyADC')
		
		Mainframe(self).pack()

App().mainloop()
Hope this helps

Roger Woollett

gillettos
Posts: 20
Joined: Fri Sep 21, 2012 8:20 am

Re: Analog I/P's and a GUI

Wed May 20, 2015 3:48 pm

Okay I'll have a butchers next week, away atm! Cheers :0)

Return to “Automation, sensing and robotics”