Automation HAT ADC issue

I’m working on a web based system to monitor broadcast station transmitter power. It’s based on Flask. My simple test Python3 script is occasionally (3 out of 10 samples) giving erroneous readings. Connecting the ADC input to the 5v terminal on the AH card, I sometimes get 0.0 or 25.6 instead of the 5v at that terminal.

Here’s the code:

#!/usr/bin/env python3

from flask import Flask, render_template
import datetime
import time

import automationhat
time.sleep(.25)

app = Flask(name)

@app.route("/")
def hello():
now = datetime.datetime.now()
timeString = now.strftime("%Y-%m-%d %H:%M")
print(timeString)

power_now = automationhat.analog.one.read()
powerString = "{:.1f} volts".format(power_now)

templateData = {
    'title' : 'HELLO!',
    'time': timeString,
    'power': powerString
    }
return render_template('main.html', **templateData)

if name == “main”:
app.run(host=‘0.0.0.0’, port=8080, debug=True)

—end code—
The script should simply return the ADC reading, but some errors appear. What am I doing wrong?

I’m afraid I’m away from the office, so I can’t replicate this, but from recollecting my previous experience with the ADC it doesn’t sound normal.

It looks like the input may be floating, and possibly coupled temporarily to the connected input. Are you sure you’re reading the same ADC input as you’ve connected your sensor to?

I’ve connected the ADC input to a 5v terminal on the HAT using a jumper wire. Oddly, the voltage reading is stable with the same hookup running analog.py from Github. It’s a head scratcher. I’m not in a time bind as the project is an early stage. Thanks.

Hmm, it’s quite possible something else is running- either in code or otherwise- that’s messing up the readings. I have a suspicion it could be some conflict between the sn3218 (that drives the LEDs) and the ADC, since they share the i2c bus and I seem to have been profoundly stupid here:

https://github.com/pimoroni/automation-hat/blob/5e32c9f6d31aa3741d8e291600cafe1f080873ba/library/automationhat/init.py#L310-L315

Which means that both of these threads are running simultaneously and potentially conflicting the bus.

If you get a moment, could you grab the code from GitHub, edit this file to remove the _auto_lights code, and install the library?

git clone https://github.com/pimoroni/automation-hat/
cd library
nano automationhat/__init__.py

Comment out the thread startup so it looks like:

#if is_automation_hat():
#    _t_auto_lights = AsyncWorker(_auto_lights)
#    _t_auto_lights.start()

And then install with:

sudo python setup.py install

And see if that fixes your ADC issues.

Gadgetoid, I commented out the three lines (near the end of the file ahead of cleanup) and did the install for both Python and Python3. Although the led’s quit blinking and only the led for ADC 1 is illuminated, the 0.0, 5.0, 25.6 returns are still occurring.

To isolate the issue, I’ve assembled a new Pi3, and a second automationhat. This testbed omits the RTC card which also uses i2c. So with just the two boards and the SD card, I find a similar but not quite identical problem. Now the readings are 0.0, 5.1, and (very occasionally) 4.7. I remain stumped, but maybe the slightly different readings will provide a clue.

BTW – I’m using Python3 in case that makes a difference.

One more thing. I just noticed that on the second testbed, no led’s are illuminated. That differs from the first, with one lit.

Gadgetoid, After playing with this awhile, I’m inclined to think it’s a ‘Flask’ problem. Running the acquisition portion of the script on its own, no errors have been noted. I’m just starting to learn Flask, so i don’t know much about its internals. So I’ll look into that.

Thanks for the help. At least with your help I’m done with the bright, blinking leds!

Jim

Ooooh. Disable reloader and that should fix it. There was a report about this somewhere else on the forums or github, I can’t remember where.

Aha! See: Explorer Hat pro flickering light when I turn it on using Flask endpoint - #6 by gadgetoid

Bingo! Thanks.

Thanks too for all the work you do for the Python community. Your dedicated efforts most especially help those of us learning the ropes.

Jim

1 Like

You’re most welcome! Happy hacking.

Continuing the discussion from Automation HAT ADC issue:

I was having an issue with the ADC flickering and showing spurious inputs. I suspected it was due to threading issues. I am pretty new to threading and Python. This fix seems to have solved my issue. Thanks!
Here is the code if anyone cares to comment…

#!/usr/bin/python3
import argparse
import math
import time
import threading
import logging
from systemd import journal

from pythonosc import dispatcher
from pythonosc import osc_server
from pythonosc import osc_message_builder
from pythonosc import udp_client

import automationhat
time.sleep(0.1) # short pause after ads1015 class creation recommended

import systemd_stopper

from threading import Thread

SERVER_ADDRESS = "0.0.0.0" # LISTENING ADDRESS
SERVER_PORT = 7110
CLIENT_ADDRESS = "tokyo.local" # SEND TO ADDRESS
CLIENT_PORT = 7111
INPUTSTATE = [0, 0, 0]
ADCSTATE = [0, 0, 0 ,0]
TIMER = 0
TIMER2 = 0

if automationhat.is_automation_hat():
	automationhat.light.power.write(1)
	automationhat.light.comms.write(0)
	automationhat.light.warn.write(0)

log = logging.getLogger('OSC Automation Hat')
log.addHandler(journal.JournaldLogHandler())
log.setLevel(logging.INFO)

def relay_handler(address, *args, needs_reply_address=False):
	automationhat.light.comms.write(1)
	print("relay "+address.split("/")[2]+" handler called")
	log.info("relay "+address.split("/")[2]+" handler called")
	relay = int(address.split("/")[2])
	relaystate = int(args[0])
	if automationhat.is_automation_hat():
		if relay == 0:
			automationhat.relay[0].write(relaystate)
		if relay == 1:
			automationhat.relay[1].write(relaystate)
		if relay == 2:
			automationhat.relay[2].write(relaystate)

def led_handler(address, *args, needs_reply_address=False):
	automationhat.light.comms.write(1)
	print("LED "+address.split("/")[2]+" handler called")
	log.info("LED "+address.split("/")[2]+" handler called")
	led = int(address.split("/")[2])
	ledstate = int(args[0])
	if automationhat.is_automation_hat():
		if led == 0:
			automationhat.light.power.write(ledstate)
		if led == 1:
			automationhat.light.comms.write(ledstate)
		if led == 2:
			automationhat.light.warn.write(ledstate)

def output_handler(address, *args, needs_reply_address=False):
	automationhat.light.comms.write(1)
	print("relay "+address.split("/")[2]+" handler called")
	log.info("relay "+address.split("/")[2]+" handler called")
	relay = int(address.split("/")[2])
	outputstate = int(args[0])
	if automationhat.is_automation_hat():
		if relay == 0:
			automationhat.output[0].write(outputstate)
		if relay == 1:
			automationhat.output[1].write(outputstate)
		if relay == 2:
			automationhat.output[2].write(outputstate)

class ADCchecker:
	def __init__(self):
		self._running = True

	def terminate(self):
		self._running = False

	def run(self):
		# CLIENT
		parser_client = argparse.ArgumentParser()
		parser_client.add_argument("--ip", default=CLIENT_ADDRESS,
				help="The ip of the OSC server")
		parser_client.add_argument("--port", type=int, default=CLIENT_PORT,
				help="The port the OSC server is listening on")
		args_client = parser_client.parse_args()

		client = udp_client.SimpleUDPClient(args_client.ip, args_client.port)
		log.info("Sending to OSC host "+CLIENT_ADDRESS)
		while self._running:
			# print("ADC check")
			i = 0
			# time.sleep(0.7)
			while (i < 3):
				ADCvalue = (automationhat.analog[i].read())
				if ((ADCSTATE[i] >= (ADCvalue + 1)) or (ADCSTATE[i] <= (ADCvalue - 1))):
					print("ADC "+str(i)+" SAVED: "+ str(ADCSTATE[i]))
					print("ADC "+str(i)+" READ: "+ str(ADCvalue))
					print()
					ADCSTATE[i] = ADCvalue
					log.info("ADC "+str(i+1)+" = "+str(ADCvalue))
					try:
						automationhat.light.comms.write(0.1)
						log.info("Sending OSC to "+CLIENT_ADDRESS+": /adc/"+str(i+1)+" "+str(ADCvalue))
						print("Sending OSC to "+CLIENT_ADDRESS+": /adc/"+str(i+1)+" "+str(ADCvalue))
						client.send_message("/adc/"+str(i+1), ADCvalue)
						log.info("success")
						automationhat.light.comms.write(0.5)
						automationhat.light.warn.write(0)
					except Exception as e:
						print('Error sending OSC: '+ str(e))
						log.info('Error sending OSC: '+ str(e))
						automationhat.light.comms.write(0)
						automationhat.light.warn.write(1)
				i = i + 1

# THREADING SETUP
CheckingADC = ADCchecker()													#Create Class
CheckingADCThread = Thread(target=CheckingADC.run)	#Create Thread
CheckingADCThread.start()														#Start Thread

if __name__ == "__main__":

	stopper = systemd_stopper.install()
	while stopper.run:
		# SERVER SETUP
		print("Starting server...")
		parser_server = argparse.ArgumentParser()
		parser_server.add_argument("--ip",
				default=SERVER_ADDRESS, help="The ip to listen on")
		parser_server.add_argument("--port",
				type=int, default=SERVER_PORT, help="The port to listen on")
		args_server = parser_server.parse_args()

		dispatcher = dispatcher.Dispatcher()
		dispatcher.map("/relay/0", relay_handler)
		dispatcher.map("/relay/1", relay_handler)
		dispatcher.map("/relay/2", relay_handler)
		dispatcher.map("/led/0", led_handler)
		dispatcher.map("/led/1", led_handler)
		dispatcher.map("/led/2", led_handler)
		dispatcher.map("/output/0", output_handler)
		dispatcher.map("/output/1", output_handler)
		dispatcher.map("/output/2", output_handler)

		server = osc_server.ThreadingOSCUDPServer(
				(args_server.ip, args_server.port), dispatcher)
		print("Server started on {}".format(server.server_address))
		log.info("OSC server started on {}".format(server.server_address))
		server_thread = threading.Thread(target=server.serve_forever)
		server_thread.start()

		# CLIENT SETUP
		parser_client = argparse.ArgumentParser()
		parser_client.add_argument("--ip", default=CLIENT_ADDRESS,
				help="The ip of the OSC server")
		parser_client.add_argument("--port", type=int, default=CLIENT_PORT,
				help="The port the OSC server is listening on")
		args_client = parser_client.parse_args()

		client = udp_client.SimpleUDPClient(args_client.ip, args_client.port)
		log.info("Sending to OSC host "+CLIENT_ADDRESS)

		while stopper.run:
			# HEARTBEAT TIMER
			if TIMER <= 5000:
				TIMER = TIMER + 1
				# print(TIMER)
			else:
				TIMER = 0
				i = 0
				while (i < 3): # SEND INPUT STATES
					INPUTSTATE[i] = automationhat.input[i].read()
					print ("INPUT "+str(i+1)+" HEARTBEAT = "+str(INPUTSTATE[i]))
					log.info("INPUT "+str(i+1)+" HEARTBEAT = "+str(INPUTSTATE[i]))
					try:
						automationhat.light.comms.write(0.1)
						log.info("Sending to "+CLIENT_ADDRESS+" OSC: /heartbeat/input/"+str(i+1)+" "+str(INPUTSTATE[i]))
						client.send_message("/heartbeat/input/"+str(i+1), INPUTSTATE[i])
						log.info("success")
						automationhat.light.comms.write(0.3)
						automationhat.light.warn.write(0)
					except:
						print("host "+CLIENT_ADDRESS+" not found")
						log.info("host "+CLIENT_ADDRESS+" not found")
						automationhat.light.comms.write(0)
						automationhat.light.warn.write(1)
					i = i + 1
				i = 0
				while (i < 3): # SEND ADC STATES
					ADCSTATE[i] = (automationhat.analog[i].read())
					print ("ADC "+str(i+1)+" HEARTBEAT = "+str(ADCSTATE[i]))
					log.info("ADC "+str(i+1)+" HEARTBEAT = "+str(ADCSTATE[i]))
					try:
						automationhat.light.comms.write(0.1)
						log.info("Sending to "+CLIENT_ADDRESS+" OSC: /heartbeat/adc/"+str(i+1)+" "+str(ADCSTATE[i]))
						client.send_message("/heartbeat/adc/"+str(i+1), ADCSTATE[i])
						log.info("success")
						automationhat.light.comms.write(0.3)
						automationhat.light.warn.write(0)
					except:
						print("host "+CLIENT_ADDRESS+" not found")
						log.info("host "+CLIENT_ADDRESS+" not found")
						automationhat.light.comms.write(0)
						automationhat.light.warn.write(1)
					i = i + 1

			# CHECK INPUT PINS
			i = 0
			while (i < 3):
				# print("checking input pin " + str(i))
				INPUTvalue = (automationhat.input[i].read())
				if (INPUTSTATE[i] != INPUTvalue):
					INPUTSTATE[i] = INPUTvalue
					print ("INPUT"+str(i+1)+" = "+str(INPUTSTATE[i]))
					log.info("INPUT"+str(i+1)+" = "+str(INPUTSTATE[i]))
					try:
						automationhat.light.comms.write(0.1)
						log.info("Sending OSC to "+CLIENT_ADDRESS+": /input/"+str(i+1)+" "+str(INPUTvalue))
						print("Sending OSC to "+CLIENT_ADDRESS+": /input/"+str(i+1)+" "+str(INPUTvalue))
						client.send_message("/input/"+str(i+1), INPUTvalue)
						log.info("success")
						automationhat.light.comms.write(0.5)
						automationhat.light.warn.write(0)
					except Exception as e:
						print('Error sending OSC: '+ str(e))
						log.info('Error sending OSC: '+ str(e))
						automationhat.light.comms.write(0)
						automationhat.light.warn.write(1)
				i = i + 1

	print("Shutting down autoPi...")
	log.info("Shutting down autoPi...")
	automationhat.light.power.write(0)
	server.shutdown()
	print("OSC server shut down")
	log.info("OSC server shut down")
	CheckingADC.terminate()