Raspberry Pi mit Spannungsüberwachung

Status
Es sind keine weiteren Antworten möglich.

steins-ursel

Aktives Mitglied
Hallo!

Damit man in der astronomisch "Sauren Gurkenzeit" nicht dem Müßiggang an heim fällt, ist mir ein neuer Gedanke gekommen. Da ich noch ein Experimentierboard mit einem ADS1115 hatte, kam ich auf die Idee, eine Spannungskontrolle mit dem Raspi aufzubauen.
Der ADS1115 ist leider nix mehr zum selber löten, da ein Ultra-Small X2QFN Package (2×1.5×0.4mm) als Gehäuse zum Einsatz kommt mit 10 Anschlussbeinchen.

Er hat 4 Eingangkanäle, welche ich wie folgt belegt habe:
K0 - CPU-Spannung vom Raspi (3,3V)
K1 - Versorgungsspannung (IO) vom Raspi (5V)
K2 - Batteriespannung (12V)
K3 - Spannung vom RTC (indirekt 2,0 bis 3,3V)
Die Belegung kann natürlich auch anders erfolgen, mit den ggf. entsprechenden nötigen Vorteilern, da 0V <= Uin <= U_Power ADS1115. Mit den Entsprechen Faktoren kann man das Ganze dann wieder auf den richtigen Ausgabewert multiplizieren.

Als Programmiersprache kommt auf Grund der schon fertigen Bibliotheken Python zum Einsatz.

1. Aber erst mal alles Vorbereiten (Installation):
Bash:
#!/bin/sh
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install python3-pip
sudo pip3 install --upgrade setuptools
sudo pip3 install RPI.GPIO
sudo pip3 install adafruit-blinka
sudo pip3 install adafruit-circuitpython-ads1x15

Hilfreich sind dabei die Seiten von Adafruit und spezifisch.

2. Python in der shell
Python:
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
from adafruit_ads1x15.ads1x15 import Mode

ads = ADS.ADS1115(i2c)
ads.gain = 2/3
ads.mode = Mode.SINGLE
print("  CPU |  IO   | Batt  | CLK  ")
while True:
    print("------|-------|-------|------")
    chan = AnalogIn(ads, ADS.P0)
    P0=chan.voltage
    chan = AnalogIn(ads, ADS.P1)
    P1=chan.voltage
    chan = AnalogIn(ads, ADS.P2)
    P2=10*chan.voltage
    chan = AnalogIn(ads, ADS.P3)
    P3=chan.voltage
    print("{:>5.3f} | {:>5.3f} | {:>5.3f} | {:>5.3f}".format(P0,P1,P2,P3))
    time.sleep(10)#nach 10 Sekunden neu einlesen

3. Python mit GUI
In der grafischen Umgebung kommt sogar noch eine Toleranzüberwachung farblich hinzu. Die Toleranzen sind gängige Wert für die einzelnen Spannungsschienen, tlw. mit den zu erwartenden Toleranzen angepasst in K2 und K3

Python:
import time
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
from adafruit_ads1x15.ads1x15 import Mode

#A/D Wandler INIT
ads = ADS.ADS1115(i2c)
ads.gain = 2/3
ads.mode = Mode.SINGLE

#GUI Init
from tkinter import *
root = Tk() # Fenster erstellen
root.wm_title("ADS1x15") # Fenster Titel
root.config(background = "#440000") # Hintergrundfarbe des Fensters

#Frames
Frbg="#550000"#Hintergrundfarbe
Frame1 = Frame(root, width=200, height = 200) # Frame initialisieren
Frame1.grid(row=0, column=0, padx=10, pady=3) # Relative Position und Seitenabstand (padding) angeben
Frame1.config(background = Frbg)

Frame2 = Frame(root, width=200, height = 100)
Frame2.grid(row=1, column=0, padx=10, pady=3)
Frame2.config(background = Frbg)

#Labels
lbbg="#660000"#Hintergrundfarbe
lbfg="#CC8888"#Vordergrundfarbe
lb1_1 = Label(Frame1, text="CPU [V]:",bg=lbbg,fg=lbfg)
lb1_1.grid(row=0, column=0, padx=10, pady=3)

lb1_2 = Label(Frame1, text="IO   [V]:",bg=lbbg,fg=lbfg)
lb1_2.grid(row=1, column=0, padx=10, pady=3)

lb1_3 = Label(Frame1, text="Batt [V]:",bg=lbbg,fg=lbfg)
lb1_3.grid(row=2, column=0, padx=10, pady=3)

lb1_4 = Label(Frame1, text="CLK [V]:",bg=lbbg,fg=lbfg)
lb1_4.grid(row=3, column=0, padx=10, pady=3)

def rw_io():
#ADS1x15 auslesen
    gv="#00CC88"#Messwert gut
    bv="#CC8800"#Messwert schlecht
    P0 = AnalogIn(ads, ADS.P0).voltage
    P0fg=gv
    if P0 < 3.14 or P0 > 3.47:#Toleranzen 3,3V
        P0fg=bv
    P1 = AnalogIn(ads, ADS.P1).voltage
    P1fg=gv
    if P1 < 4.75 or P1 > 5.25:#Toleranzen 5V
        P1fg=bv
    P2 = AnalogIn(ads, ADS.P2).voltage * 10
    P2fg=gv
    if P2 < 11.4 or P2 > 14.4:#Toleranzen 12V
        P2fg=bv
    P3 = AnalogIn(ads, ADS.P3).voltage
    P3fg=gv
    if P3 < 2 or P3 > 3.47:#Toleranzen GoldCap(RTC)
        P3fg=bv
 
#Werte Ausgeben
    lb1_11 = Label(Frame1, text="{:>5.3f}".format(P0),fg=P0fg,bg=lbbg)
    lb1_11.grid(row=0, column=1, padx=10, pady=3)
    lb1_12 = Label(Frame1, text="{:>5.3f}".format(P1),fg=P1fg,bg=lbbg)
    lb1_12.grid(row=1, column=1, padx=10, pady=3)
    lb1_13 = Label(Frame1, text="{:>5.3f}".format(P2),fg=P2fg,bg=lbbg)
    lb1_13.grid(row=2, column=1, padx=10, pady=3)
    lb1_14 = Label(Frame1, text="{:>5.3f}".format(P3),fg=P3fg,bg=lbbg)
    lb1_14.grid(row=3, column=1, padx=10, pady=3)
#GUI-Schleife
    Frame1.after(10000, rw_io)#alle 10s neu einlesen

rw_io() #Initial Lesen und Ausgeben
 
buttonFrame = Frame(Frame2)
buttonFrame.grid(row=4, column=0, padx=10, pady=3)
B1 = Button(buttonFrame, text="Quit", bg="#880000", width=8, command=root.destroy)
B1.grid(row=0, column=0, padx=10, pady=3)

root.mainloop() # GUI wird upgedated. Danach keine Elemente setzen

Gerade für oft ferngesteuerte Setups eine Hilfreiche Informationsquelle über den Status der Stromversorgung. Sobald die HW-richtig fertig ist, gibt es aich noch ein paar Bilder und Anmerkungen

BTW: Da ich kein Python-Programmierer vor dem Herrn bin, ist dieser Code als Beispiel-Code zu verstehen, es geht bestimmt besser.
 

Anhänge

  • AD_GUI.png
    AD_GUI.png
    13,6 KB · Aufrufe: 234
  • AD_Shell.png
    AD_Shell.png
    42,1 KB · Aufrufe: 203
  • RaspBiSideAD.pdf
    RaspBiSideAD.pdf
    17,2 KB · Aufrufe: 231
Zuletzt bearbeitet:
Hallo!
Zum Verständnis trägt auch die Seite bei.
 
Hallo!
Die Spannungsüberwachung ist nun fertig. Die Hardware musste soweit geändert werden, dass das SideBoard, welche die GPIO-Anschlüsse herraus führt, geändert werden musste. Die Platine des ADS1115 ist "huckepack" aufgelötet worden. Alles musste schön klein sein.
Die Software wurde nun noch soweit geändert, das eine andere Bibliothek genutzt wird, um im dunkeln auch ein Abweichen der Batteriespannung mittels LED über den Alert Ausgang als Blinklicht zu signalisieren. Die LED ist zwar sehr Dunkel, in der Nacht ist die Helligkeit mehr als ausreichend.
Das Applet hat jetzt einen Schieberegler, mittels den man die Zeit zwischen den Messungen verändern kann.

Code in der finalen Fassung:
Python:
#!/usr/bin/env python3
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
from ADS1x15 import ADS1115

#A/D Wandler INIT
ads = ADS1115()
GAIN=1
volt=4.096/32767 # bei Gain 1
kr=1.002#Spannungdkorrektur 12V
gv="#00FF88"#Messwert gut
bv="#FF8800"#Messwert schlecht

#GUI Init
from tkinter import *
root = Tk() # Fenster erstellen
root.wm_title("ADS1x15") # Fenster Titel
root.config(background = "#440000") # Hintergrundfarbe des Fensters
fh=13 #Fontgröße

#Frames
Frbg="#550000"
Frame1 = Frame(root, width=200, height = 200) # Frame initialisieren
Frame1.grid(row=0, column=0, padx=10, pady=3) # Relative Position und Seitenabstand (padding) angeben
Frame1.config(background = Frbg)

Frame2 = Frame(root, width=200, height = 100)
Frame2.grid(row=1, column=0, padx=10, pady=3)
Frame2.config(background = Frbg)

lbbg="#660000"#Background Color
lbfg="#CC8888"#Foreground Color

lb2_1 = Label(Frame2, text="next Convert [s]",bg=lbbg,fg=lbfg)
lb2_1.grid(row=0, column=0, padx=3, pady=3)

buttonFrame = Frame(Frame2)
buttonFrame.grid(row=2, column=0, padx=3, pady=3)
B1 = Button(buttonFrame, text="Quit", bg="#880000", fg="#DDDDDD", width=8, command=root.destroy)
B1.grid(row=2, column=0, padx=3, pady=3)

Zeit = Scale(Frame2, from_=0.5, to=20, resolution=0.5, orient=HORIZONTAL, length=150,bg="#770000",fg="#DDDDDD")
Zeit.grid(row=1, column=0, padx=3, pady=3)
Zeit.set(1)#Standart 10s

#Labels
lb1_1 = Label(Frame1, text="CPU [V]:   ",font=('arial',fh),bg=lbbg,fg=lbfg)
lb1_1.grid(row=0, column=0, padx=3, pady=3)

lb1_2 = Label(Frame1, text="IO   [V]:   ",font=('arial',fh),bg=lbbg,fg=lbfg)
lb1_2.grid(row=1, column=0, padx=3, pady=3)

lb1_3 = Label(Frame1, text="Batt [V]:   ",font=('arial',fh),bg=lbbg,fg=lbfg)
lb1_3.grid(row=2, column=0, padx=3, pady=3)

lb1_4 = Label(Frame1, text="CLK [V]:   ",font=('arial',fh),bg=lbbg,fg=lbfg)
lb1_4.grid(row=3, column=0, padx=3, pady=3)

lb1_11 = Label(Frame1, text="0.000",font=('arial',fh),bg=lbbg)
lb1_11.grid(row=0, column=2, padx=3, pady=3)
lb1_12 = Label(Frame1, text="0.000",font=('arial',fh),bg=lbbg)
lb1_12.grid(row=1, column=2, padx=3, pady=3)
lb1_13 = Label(Frame1, text="0.000",font=('arial',fh),bg=lbbg)
lb1_13.grid(row=2, column=2, padx=3, pady=3)
lb1_14 = Label(Frame1, text="0.000",font=('arial',fh),bg=lbbg)
lb1_14.grid(row=3, column=2, padx=3, pady=3)

def rw_io():
#ADS1x15 auslesen
    P0 = ads.read_adc(0, gain=GAIN) * volt
    P0fg=gv
    if P0 < 3.14 or P0 > 3.47:#Toleranzen 3,3V
        P0fg=bv
    P1 = ads.read_adc(1, gain=GAIN) * volt * 24/12#Teiler reverse für 5V
    P1fg=gv
    if P1 < 4.75 or P1 > 5.25:#Toleranzen 5V
        P1fg=bv
    ads.start_adc_comparator(2, 23851, 18882, active_low=True, traditional=False, latching=False ,num_readings=1, gain=GAIN)
    P2 = ads.read_adc(2, gain=GAIN) * volt * kr * 227/47#Teiler reverse für 12V
    P2fg=gv
    if P2 < 11.4 or P2 > 14.4:#Toleranzen 12V
        P2fg=bv
    P3 = ads.read_adc(3, gain=GAIN) * volt
    P3fg=gv
    if P3 < 2 or P3 > 3.47:#Toleranzen GoldCap(RTC)
        P3fg=bv
    ads.stop_adc()
#Werte Ausgeben
    lb1_11.configure(text="{:>5.3f}".format(P0),fg=P0fg)
    lb1_12.configure(text="{:>5.3f}".format(P1),fg=P1fg)
    lb1_13.configure(text="{:>5.3f}".format(P2),fg=P2fg)
    lb1_14.configure(text="{:>5.3f}".format(P3),fg=P3fg)
#GUI-Schleife 
    Frame1.after(int(Zeit.get()*1000), rw_io)
  
rw_io() #Initial Lesen und Ausgeben
  
root.mainloop() # GUI wird upgedated. Danach keine Elemente setzen
 

Anhänge

  • AD_GUI02.png
    AD_GUI02.png
    12,8 KB · Aufrufe: 207
  • SB_AD01.JPG
    SB_AD01.JPG
    306 KB · Aufrufe: 159
  • SB_AD02.JPG
    SB_AD02.JPG
    230,3 KB · Aufrufe: 200
Hallo!
Durch Versuche, die Überwachung sinnvoll mit auf den Desktop zu bringen sind noch ein paar Änderungen eingeflossen.
1. Das Widget ist nun als Zeile dargestellt.
1609501033369.png

Die Batteriespannung hier wird aus einem ungeregeltem Netzteil erzeugt, deswegen ~16V

2.Sobald ein Wert den Toleranzrahmen verlässt (hat ja einen Grund), drängelt sich die Ausgabe in den Vordergrund, um auf den Fehler hinzuweisen, d.h. man kann eine Abweichung vom tolerierten Soll nicht mehr übersehen. I.d.R. wird es die zu schwache Batterie sein. Solange der Fehler besteht, bleibt das Widget im Vordergrund. Dazu wird beim Verlassen der Toleranz ein Fehlerflag gesetzt, was dann ausgewertet wird.
Python:
    if Alarm == True:
        root.attributes("-topmost", True)
    else:
        root.attributes("-topmost", False)
    root.lift
 
Status
Es sind keine weiteren Antworten möglich.
Zurück
Oben