steins-ursel

Aktives Mitglied
Hallo!

AstroRaspi4 Version 2 ist fertig. Nach gut 2 Monaten tüfteln, einigen Rückschlägen (Prototyp 1 und 2) läuft die Sache. Angeregt wurde ich durch diesen Thread. Und Dank einer informativen Diskussion mit Roger, alias GanymedRN, wurden noch einige Fragen gelöst.
Um auf eine Platinengröße zu kommen, welche der Grundfläche des Raspberry Pi (ca. 60x87mm) entspricht, geht es nur mit SMD-Bauteilen. Die Bauteilgröße ist allerdings so gewählt, das diese mit Amateurmitteln noch lötbar sind. Obwohl die beiden SOT23 6 polig schon arg grenzwertig sind.
Vorstellung der Hardware:

Folgende Features sind umgesetzt worden:
(von AstroRaspi4 V1)
-SV 5V 4A mit LM2678S
-RTC mit DS1307 mit GoldCap 0,47F als "Stützbatterie"
-GPS über RS232 / Bluetooth
-Spannungsüberwachung mit ADS1115
-LCD-Anzeige über lcdprog
-Phyton Oberfläche zur Information der Spannungsüberwachung
-autarke Lüftersteuerung

Neu hinzu kommen (Version 2):
-Verpolschutz mit CMOS P-Kanal (Schottkydiode hatte zuviel Verluste)
-2 Kanäle Dauerplus 12V als Ersatz für die Anschlussbox an der Montierung
-2 steuerbare PWM Kanäle (GPIO6 und 13) niederfrequent bis max. 200Hz z.B. für Heizung
-1 steuerbarer PWM Kanal hochfrequent mit L-C-D Siebglied für Flatfieldbox
-alles auf einer Platine, um den Kabel- und Platinenzoo gering zu halten, außer LCD
-alles auf Basis von Hohlsteckern 5,5mm/2,1mm mit Verriegelung
-SV für 3,3V getrennt von der 3,3V Versorgung vom Raspberry
-Python Oberfläche erweitern zur Steuerung der 3+1 PWM-Kanäle
-Durch einen PWM-Kanal zusätzlich ansteuerbare Lüftersteuerung (GPIO 12)
-Anzeige einer Spannungsausgabe durch LED's rot an den Hohlsteckerbuchsen
-Gehäuse adaptierbar an einer Prismenklemme

Die zwei niederfrequenten PWM-Kanäle (GPIO 6 und 13) vom Raspberry werden über einen Logig-N-MOS (Danke an GanymedRN) verstärkt. Diese Vertragen 5,2A @ 25°C, reicht für die meisten Heizbänder.

Die Steuerung des hochfrequenten Kanals läuft über den I2C-Bus, ein MCP4725 (DAC 1 Kanal) gibt eine Spannung von 0V bis 1V über einen Spannungsteiler aus, ein LTC6992 wandelt diese als HF-PWM im Bereich von 270kHz von 0-100%. Der MCP14E11 treibt den Endstufen CMOS. Dieser verträgt bis zu 20A, überlebt also einiges. Der Tiefpass und Gleichrichter aus L-C-D glättet die HF als fast Gleichspannung, damit keine Interferenzen auf dem Flat erscheinen (Danke an GanymedRN).
Es gibt auch die Möglichkeit einen der 2 Hardware-PWM's des Raspberry zu nutzen (Auflöung 1ns), da sind aber zusätzliche Eingriffe in das System notwendig.

Durch die zusätzlichen Logik-Verbraucher ist eine eigene 3,3V Versorgung erforderlich geworden, um die eigene vom Raspi nicht zu überlasten.
Einen Fehler enthält die Schaltung dennoch, die 3,3V für den Gold-Cap (für RTC) gewinne ich noch aus den 3,3V vom Raspi, obwohl es recht einfach war, die Schaltung zu ändern. Man denkt halt nicht an alles.

Der Lüfter kann nun noch zusätzlich über einen weiteren PWM-Kanal (GPIO 12) manuell angesteuert werden.

Zur Ansteueranzeige der Buchsen sind 4 zusätliche LED vorhanden, welche parallel zu den Hohsteckerbuchsen geschaltet sind. Sie befinden sich zwischen den Buchsen als SMD-Reverse-Mount in rot. Die Vorwiderstände sind auf Grund der hohen Leuchtkraft der LED's sehr hochohmig (68kOhm).

Bei den Hohlsteckerbuchsen bitte aufpassen. Hier muss der Stecker passen, ansonsten gibt es kurzzeitig einen Kurzschluss beim Einstecken, und kann unter Umständen das ganze System kurzzeitig reseten.

Die LCD-Anzeige ist nun drehbar gelagert und kann der jeweiligen Situation angepasst werden. Trockenübungen mit der Version 2 sind vielversprechend.

Im Anhang ein PDF für den Werdegang, die Schaltung und die Platine mit Vorder- und Rückseite.
 

Anhänge

  • RaspiBoard04.sch.pdf
    36,6 KB · Aufrufe: 317
  • RaspiBoard04.top.brd.pdf
    72,6 KB · Aufrufe: 186
  • RaspiBoard04.bot.brd.pdf
    46,7 KB · Aufrufe: 182
  • DSC_0753.JPG
    DSC_0753.JPG
    166,6 KB · Aufrufe: 445
  • Version2.pdf
    1,7 MB · Aufrufe: 323
Zuletzt bearbeitet:
Die Software
Die Software ist in Python geschrieben.
Die Installation der zusätzlichen Bibliotheken ist mit thonny recht einfach. Unter Extras, Verwaltete Bibliotheken, kann man die zusätzlich benötigten Sachen installieren, Internetverbindung vorausgesetzt.

Das Pythonscript gliedert sich in Initialisierung, festlegen von Funktionen, Einlesen von gespeicherten Werten der einzelnen Kanäle, kreieren der grafischen Ausgabe, und Start des eigentlichen Widget's. Alle 3 Sekunden wird eine Spannungsmessung durchgeführt. Die Spannungstoleranzen sind von den gängigen definierten Spannungsfenstern abgeleitet. Das Widget drängelt sich in den Vordergrund, sobald ein Wert den Bereich verlässt. Die Position auf den Desktop ist für Kstars/EKOS optimiert.
K1 und K2 sind die NF PWM, FN die Übersteuerung vom Lüfter und FT die Steuerung für Flatfieldbox.

Über den Button Save werden alle 4 Werte in eine Datei (kanal.conf) geschrieben, welche vorher manuell anzulegen ist mit folgenden Inhalt.
Code:
0
0
0
0

Zusätzlich wird außerdem der EEPROM des DAC MCP4725 mit dem eingestellten Wert beschrieben, um beim Einschalten diesen automatisch zu laden und auszugeben.

Hier der Python-Code:

Python:
#!/usr/bin/env python3
import board
import busio
import RPi.GPIO as GPIO
import adafruit_mcp4725 #DAC
from ADS1x15 import ADS1115
i2c = busio.I2C(board.SCL, board.SDA) #i2c-bus
ads = ADS1115() #ADC 4x
dac = adafruit_mcp4725.MCP4725(i2c,address=0x60) #DAC

#init GPIO
#K0 = 5  #Kanal 0 - GPIO5
K1 = 6  #Kanal 1 - GPIO6 / Hzg1
K2 = 13 #Kanal 2 - GPIO13 / Hzg2
K3 = 12 #Kanal 3 - GPIO12 / FAN
FRQ = 100 #100Hz

#Function PWM-Out
def K1_OUT(angle):
    global K1DC
    K1DC=angle
    K1W.ChangeDutyCycle(int(angle))

def K2_OUT(angle):
    global K2DC
    K2DC=angle
    K2W.ChangeDutyCycle(int(angle))

def K3_OUT(angle):
    global K3DC
    K3DC=angle
    K3W.ChangeDutyCycle(int(angle))

def K4_OUT(angle):  #PWM else DAC
    global K4DC
    K4DC=angle
#   K4W.ChangeDutyCycle(int(angle)) #PWM
    dac.normalized_value = int(angle)/100 #DAC

#Werte sichern
def sichern():
    kanalw=open('./kanal.conf','w')
    kanalw.write(str(K1DC))
    kanalw.write('\r\n')
    kanalw.write(str(K2DC))
    kanalw.write('\r\n')
    kanalw.write(str(K3DC))
    kanalw.write('\r\n')
    kanalw.write(str(K4DC))
    kanalw.close()
    dac.save_to_eeprom()

#letzte Werte lesen
kanal=open('./kanal.conf','r+')
K1DC = int(kanal.readline())
K2DC = int(kanal.readline())
K3DC = int(kanal.readline())
K4DC = int(kanal.readline())
kanal.close()

GPIO.setmode(GPIO.BCM)
#for i in {K0, K1, K2, K3, K4}: #PWM
for i in {K1, K2, K3}:  #DAC
    GPIO.setup(i, GPIO.OUT)

K1W = GPIO.PWM(K1, FRQ) # K1
K1W.start(K1DC) # Initialisierung

K2W = GPIO.PWM(K2, FRQ) # K2
K2W.start(K2DC) # Initialisierung

K3W = GPIO.PWM(K3, FRQ) # K3
K3W.start(K3DC) # Initialisierung

dac.normalized_value = int(K4DC)/100

#A/D Wandler INIT
GAIN=1
volt=4.096/32767 # bei Gain 1
kr=1.002 #Korrektur für Anzeige
gv="#00FF88" #Messwert gut
bv="darkred" #Messwert schlecht
bbg="#BBBB00"
alarm = False

#GUI Init
from tkinter import *
root = Tk() # Fenster erstellen
root.wm_title("AstroRaspi PowerControl") # Fenster Titel
root.config(background = "#440000") # Hintergrundfarbe des Fensters
Ww = 810 # Widgetbreite
Wh = 45 #
SCL = 65 # ScaleWidth
sw = root.winfo_screenwidth()-Ww-150 #x-Position ermitteln
sh = root.winfo_screenheight()-Wh-55 #y-Position ermitteln
# width x height + x_offset + y_offset
root.geometry(str(Ww)+"x"+str(Wh)+"+"+str(sw)+"+"+str(sh)) #Größe und Psoition setzen
fh=12 #Fontgröße
fhl=fh-3
scw=10 #Höhe Schieberegler
t=3000 #alle 3000ms neu messen

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

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

#Wertebeschriftungen
lb1_1 = Label(Frame1, text="3V3:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_1.grid(row=0, column=0, padx=3, pady=3)

lb1_2 = Label(Frame1, text="5V0:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_2.grid(row=0, column=2, padx=3, pady=3)

lb1_3 = Label(Frame1, text="Bat:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_3.grid(row=0, column=4, padx=3, pady=3)

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

#Werteausgabe erstellen
lb1_11 = Label(Frame1, text="0.000",font=('arial',fh),bg=lbbg)
lb1_11.grid(row=0, column=1, padx=3, pady=3)

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

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

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

#Kanaele beschriften
K1L = Label(Frame1, text="K1",font=('arial',fhl),bg=lbbg,fg=lbfg)
K1L.grid(row=0, column=12, padx=3, pady=3)

K2L = Label(Frame1, text="K2",font=('arial',fhl),bg=lbbg,fg=lbfg)
K2L.grid(row=0, column=14, padx=3, pady=3)

K3L = Label(Frame1, text="FN",font=('arial',fhl),bg=lbbg,fg=lbfg)
K3L.grid(row=0, column=16, padx=3, pady=3)

K4L = Label(Frame1, text="FT",font=('arial',fhl),bg=lbbg,fg=lbfg)
K4L.grid(row=0, column=18, padx=3, pady=3)


#Scales
#Kanal1 nur PWM
K1S = Scale(Frame1, from_=0, to=100, resolution=5, orient=HORIZONTAL, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8),command=K1_OUT,width=scw)
K1S.set(K1DC)
K1S.grid(row=0, column=13, padx=3, pady=3)
#Kanal2 nur PWM
K2S = Scale(Frame1, from_=0, to=100, resolution=5, orient=HORIZONTAL, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8),command=K2_OUT,width=scw)
K2S.set(K2DC)
K2S.grid(row=0, column=15, padx=3, pady=3)
#Kanal3 Luefter
K3S = Scale(Frame1, from_=0, to=100, resolution=10, orient=HORIZONTAL, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8),command=K3_OUT,width=scw)
K3S.set(K3DC)
K3S.grid(row=0, column=17, padx=3, pady=3)
#Kanal4 Flat mit LED-Treiber
K4S = Scale(Frame1, from_=0, to=100, resolution=5, orient=HORIZONTAL, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8),command=K4_OUT,width=scw)
K4S.set(K4DC)
K4S.grid(row=0, column=19, padx=3, pady=3)

SiBu=Button(Frame1,text="Save",command=sichern, bg="#770000",fg="#DDDDDD", font=('arial',8))
SiBu.grid(row=0,column=20, padx=3, pady=3)

def rw_io():  #ADS1x15 auslesen
    Alarm = False
    P0 = ads.read_adc(0, gain=GAIN) * volt
    P0fg=gv
    P0bg=lbbg
    if P0 < 3.14 or P0 > 3.47:#Toleranzen 3,3V
        P0fg=bv
        P0bg=bbg
        Alarm =True
    P1 = ads.read_adc(1, gain=GAIN) * volt * 24/12#Teiler reverse für 5V
    P1fg=gv
    P1bg=lbbg
    if P1 < 4.75 or P1 > 5.25:#Toleranzen 5V
        P1fg=bv
        P1bg=bbg
        Alarm =True
    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
    P2bg=lbbg
    if P2 < 11.4 or P2 > 14.4:#Toleranzen 12V
        P2fg=bv
        P2bg=bbg
        Alarm =True
    P3 = ads.read_adc(3, gain=GAIN) * volt
    P3fg=gv
    P3bg=lbbg
    if P3 < 2 or P3 > 3.47:#Toleranzen GoldCap(RTC)
        P3fg=bv
        P3bg=bbg
    ads.stop_adc()
#Werte Ausgeben
    lb1_11.configure(text="{:>5.3f}".format(P0),fg=P0fg,bg=P0bg)
    lb1_12.configure(text="{:>5.3f}".format(P1),fg=P1fg,bg=P1bg)
    lb1_13.configure(text="{:>5.3f}".format(P2),fg=P2fg,bg=P2bg)
    lb1_14.configure(text="{:>5.3f}".format(P3),fg=P3fg,bg=P3bg)
    if Alarm == True:
        root.attributes("-topmost", True)
    else:
        root.attributes("-topmost", False)
#GUI-Schleife  
    Frame1.after(t, rw_io)
   
rw_io() #Initial Lesen und Ausgeben
   
root.mainloop() # GUI wird upgedated. Danach keine Elemente setzen

P.S. Das Projekt soll Anderen als Anregung dienen, Nachbau im Privatbereich sogar erwünscht.

Viel Spaß, beim Basteln ;-)
 

Anhänge

  • pwrc.jpg
    pwrc.jpg
    30,7 KB · Aufrufe: 420
Hallo!
Erste Erfahrungen zeigen, dass noch einiges geändert werden kann.
1. Die Leuchtkraft aller roten LED's ist durch das durchsichtige Gehäuse richtig gut. Die PWR-Led ist nun abgeschaltet, die Kontroll-LED's an den Buchsen werde ich mal 100k Vorwiderstand probieren, 68k sind noch zu klein.
2. Die Buchsen sind bei großen Teleskopen gut erreichbar, bei kleineren ist der Abstand zur Montierung grenzwertig, wenn man die Stecker "eintüten" will. Gerade die USB 3.0 A-Stecker sind verflucht lang und liegen bauartbedingt durch den Raspi genau in der Mitte. Die Hohlstecker sind ggf. besser seitlich anzuordnen, um sie besser zu Erreichen, damit fällt aber der Schutz gegen Betauung schlechter aus je nach Lage des Teleskopes.
3. Die Lüftersteuerung ist sehr nervös, ich werde den Hysteresewiderstand von 2,2MOhm auf 1M wieder herabsetzen.

Entgegen allen Bedenken ist der GPS Empfang gerade bei der "Initialisierung" des Setups richtig gut. Da später auf das GPS der Wetterstation umgeschaltet wird, war die Entscheidung der Anordnung des GPS genau richtig.
 
zieh die mal diesen Link rein, erklärt hoffentlich einiges.Grundlage des Systems: Raspbian, INDI/KSTARS/EKOS ...
also die komplette Steuerung des Teleskopes/Kamera/Guiding/Heizung.... über den Raspi per WLAN
Ich wollte nochmal kurz zurück zu diesem Thema. Ein DT-06 hab ich jetzt bestellt und die Idee find ich erstmal auch super. Aber diese Sache mit kompletter Steuerung und Guiding und Fotos per RP ist eine Bastelei nach meinem Geschmack. Nach etwas Recherche hab ich jetzt zwei systeme gefunden: Stellar Mate und Astropi. Allerdings wäre für beide Lösungen keine extra Platine und Programmieren in Phyton nötig gewesen. AstroRaspi4 hat ja offensichtlich eine deutlich erweitere Funktionalität die sich vor allem auf die Stromversorgung (?) bezieht?! Lohnt sich der Aufwand überhaupt? Das wirkt ja wirklich als ware es eine ziemliche Arbeit das teil zu bauen.

Besten Dank.
 
Hallo!
Die Namensgebung "AstroRaspi4" ist ein Konstrukt von mir. Irgendwie musste ich das Teil ja nennen.
Die Stromversorgung für einen Computer ist eine sehr sensible Sache und sollte robust und stabil sein. Da habe ich im Vorfeld einige Chips getestet, und bin bei der Schaltung gelandet. Der größte Aufwand war die Entwicklung der Platine als eine Platine aus bis zu 3 Einzelplatinen (V1) zu gestalten und die neuen Ideen (PWM ...) noch zu integrieren. Dazu noch so wenig wie möglich Durchkontaktierungen, auf Grund der Komplexität ging es nicht ohne. Auch musste das Ganze auf der eigenen Isolierfräse sinnvoll laufen. Das Löten war dann noch der einfachste Teil, strukturiert vorgehen, einen Schaltungsteil einlöten und in Betrieb nehmen, um Fehler schon beim Einlöten zu erkennen.
Die Software ist wie schon erwähnt Raspbian Buster, Kernel 64Bit, INDI und KSTARS/EKOS nur das, was an Treiber wirklich gebraucht wird.
Als Basis hatte ich mir mal das Script AstroPi3 hergenommen. Dies kann man auch zur Installation nutzen, nur das die Compilerorgie dann gut 2,5h dauert.
Ja, da ich schon sehr lange und gerne mit Elektronik bastle. Vom Lernen will ich gar nicht reden
 
Zuletzt bearbeitet:
Hallo!
Ein SHT31D erregte meine Aufmerksamkeit beim Stöbern im Internet, da ich immer noch auf der Suche nach einem Feuchtigkeitssensor war, welcher als zusätzlichen Steuerung für die Heizbänder dienen soll. Der von Adafruit, welche Bibliotheken ich nutze, war mir leider zu groß.
Die Installation der zusätzlichen Bibliotheken ist über Thonny sehr komfortabel, nach einem Stichwort unter Extras - Verwaltete Pakete suchen und die Bibliothek installieren.
Die Hardware musste nur so angepasst werden, dass die beiden Pull-Up-Widerstände a 10kOhm für den I2C entfernt werden müssen, da dies der Raspberry schon mit bringt. Die kleine Platine läuft somit auf 5V, der I2C auf 3,3V. Mittlerweile hängen da 5 IC's am I2C dran, bisher ohne stottern oder gar Ausfälle.
Der Sensor ist oberhalb des LCD-Display angebracht, er kann somit direkt die Feuchtigkeit und Temperatur messen, welche Objektiv anliegen. Die Steuerung ist so programmiert, dass ich über einen der beiden Kanäle einen Grundwert einstelle und der Regler "HO" zusätzlich den Umfang bestimmt, wie stark der Feuchtigkeitssensor wirkt von 0-100%. Muss mal sehen, ob das so funktioniert, wie ich mir das vorstelle.
Anbei die Bilder von der Weiterentwicklung.
 

Anhänge

  • DSC_0761.JPG
    DSC_0761.JPG
    240,6 KB · Aufrufe: 264
  • DSC_0762.JPG
    DSC_0762.JPG
    346,5 KB · Aufrufe: 246
  • control.jpg
    control.jpg
    33,8 KB · Aufrufe: 301
Hallo!
Nach einem ersten Test an der frischen Luft, bin ich persönlich erst mal zufrieden mit dem Ergebnis. Selbst die Steuerung der Heizbänder geführt über den SHT31D klappt wunderbar. Grundwert einstellen - Kennlinie festlegen - los geht's. Taubeschlag gestern Abend -> Fehlanzeige. Auch sind die Lichtstreuungen des Raspi's recht gering, eine Störung beim Langzeitbelichten ist beim EDPH76 nicht aufgefallen.
Die GPS-Verbindung, selbst über den BN-220, ist stabil die ganze Session gewesen. Als finales USB-3.0-Kabel habe ich nun ein Lindy-Kabel gefunden, leider im Ausverkauf, wichtig sind mir dabei: kurze Stecker. Durch die konsequente Verkabelung direkt am Teleskop entfallen einige Kabelstrecken, welche nun nicht mehr die Bewegung ausgleichen müssen. Es sind nur noch: SV, Guidingkabel, USB-Kabel zur Montierung. Vorher mind. doppelt so viele mit mehr Chancen zum "hängen" bleiben.

Fazit: Aufwand hat sich gelohnt und wieder viel gelernt.

BTW: Nur der kleine APO wirkt irgendwie deplatziert auf der EQ6r :)
 

Anhänge

  • DSC_0757.JPG
    DSC_0757.JPG
    305,4 KB · Aufrufe: 307
Zuletzt bearbeitet:
Den oben erwähnten Temperatursensor habe ich letztens auch entdeckt. Bisher hatte ich meine kleine Wetterstation mit einem BMP280 ausgestattet. Leider ist mein Exemplar ziemlicher Müll. Ich vermute, dass es kein Original BOSCH Sensor ist. Während Luftdruck sehr genau ist, weichen Temperatur und Feuchtigkeit stark von anderen Referenz-Sensoren ab. Leider ist der Kennlinie auch nicht linear. Ich hätte mir da eine Korrekturtabelle im Arduino ablegen müssen. Na ja, die Wetterstation ist eigentlich nur ein Abfallprodukt der Bastelei, da es mir primär um die Regendetektion mit einem anderen Sensor ging, der meine Rollos steuert.

Dein Aufbau sieht sehr kompakt aus und in der Tat merkt die Montierung wohl kaum, wer da auf ihrem Rücken kitztelt. :D

Ich spiel auch mit dem Gedanken, mir im laufe des Jahres mal eine Refraktor zuzulegen. Das draufwuchten des 200PDS/1000 ist immer ein ziemlicher Akt in Verbindung mit dem anschliessenden ausbalancieren. :cautious:

Was die Verkabelung betrifft, bin ich jetzt auch soweit, dass es nur noch ein Kabel gibt, dass an der Montierung runterhängt. Nachdem ich ja direkt an der Montierungsseite meiner EQ6-R den RPi + USB 3.0 -Hub angschraubt hatte, habe ich nun in das ankommende 12V (6A) Power-Kabel der Montierung ein Stück Verlängerung "eingeschraubt" und das als Y-Kabel ausgelegt. Von dort gehts zur Montierung und im anderen Zweig in einen StepDown zum RPi.

Grüße
Hartmut
 
Was die Verkabelung betrifft, bin ich jetzt auch soweit, dass es nur noch ein Kabel gibt, dass an der Montierung runterhängt. Nachdem ich ja direkt an der Montierungsseite meiner EQ6-R den RPi + USB 3.0 -Hub angschraubt hatte, habe ich nun in das ankommende 12V (6A) Power-Kabel der Montierung ein Stück Verlängerung "eingeschraubt" und das als Y-Kabel ausgelegt. Von dort gehts zur Montierung und im anderen Zweig in einen StepDown zum RPi.
Hallo Hartmut,
ausschlaggebend für die Neuorientierung des Raspberry Projekts waren auch 2 Sachen:
1. ich hatte mal in einem Bericht über die komplett ferngesteuerten kleinen Mietteleskope gesehen, und sah dort, dass man zum Schluss vor lauter Kabel die Teleskope tlw. nur noch darunter erahnen konnte.
2. Durch einen Kabelhänger im Dunkeln hätte ich mir fast mal die ASI2600 zerlegt, dass war der wesentliche Trigger für das Umdenken. (Das Motto "Lernen durch Schmerzen" wollte ich dabei nicht durchleben)
Dazu noch einige Anregungen hier im Forum .. und es gab kein halten mehr.
 
Ha, ich hatte ähnliches erlebt. Das Power-Kabel meiner damals noch externen Stromversorgung für den WiFi Adapter hatte sich um die Gegengewichtsstange gewickelt und die Adern abgerissen. Beim nächsten mal hat sich dann das Power-Kabel vom USB-Hub verheddert und den Stecker um 45° gebogen, bvor ich alles anhalten konnte. Das fällt beim probieren tagsüber noch auf, aber nachts und dann noch remote..., da hört man nicht mal das ächtzen der Kabel, wenn sie sich längen...
 
... oder man sieht den Kabelhänger dann irgendwann in den Bildern das die Nachführung irgendwie nicht mehr funktioniert hat. Ich glaube da sind alle schon mal durch o_O :LOL:.
Ich habe mir jetzt auch einen Mini PC und einen Stromverteiler direkt ans Teleskop gebaut. Der Kabelsalat hat sich massiv minimiert und da das Ganze als Remote Setup gedacht ist werde ich das wohl auch so in der Astrohütte installieren.
 
Hallo!
Angeregt durch diesen Thread,, habe ich mal eine einfache Funktion in Python für den Taupunkt erstellt. Ich werde diese dann in die Steuerungssoftware noch einpflegen. Aus diesem Grund habe ich die Funktion auch hier eingebettet. Die Test-Ergebnisse decken sich mit dem Rechner auf der Quellseite.

Python:
import math

def DewPoint(t,hr):
    if t >= 0:
        a = 7.5
        b = 237.3
    elif t < 0:
        a = 7.6
        b = 240.7
    sdd = 6.1078 * 10**((a*t)/(b+t))
    dd = hr/100 * sdd
    v = math.log10(dd/6.1078)
    tp = b*v/(a-v)
    return(round(tp,1))
      
T = -10
HR = 50
TP = DewPoint(T,HR)
print(TP)

Quelle
 
Zuletzt bearbeitet:
Hallo!

Damit diese Anzeige per Python auf dem Raspi angezeigt wird (mit Warnung, wenn Taupunkt nahe an die Umgebungstemperatur kommt)
1626204891295.png

ist dieser Quelltext nötig:

(Ob das im Endeffekt mit der logarithmischen Kennlinie so passt, kann nur der Test im Feld zeigen)

Python:
#!/usr/bin/env python3
import os
import math
import board
import busio
import RPi.GPIO as GPIO
import adafruit_mcp4725 #DAC
import adafruit_sht31d #Humity

from ADS1x15 import ADS1115 #ADC x4
from gpiozero import CPUTemperature

i2c = busio.I2C(board.SCL, board.SDA) #i2c-bus
ads = ADS1115() #ADC 4x
hus = adafruit_sht31d.SHT31D(i2c)
dac = adafruit_mcp4725.MCP4725(i2c,address=0x60) #DAC
cpu = CPUTemperature()

#init GPIO
#K0 = 5  #Kanal 0 - GPIO5
K1 = 6  #Kanal 1 - GPIO6
K2 = 13 #Kanal 2 - GPIO13
K3 = 12 #Kanal 3 - GPIO12 / FAN
#K4 = 23 #Kanal 4 - GPIO23 / Flat #PWM
FRQ = 100 #100Hz
Bits=16

def DewPoint(tc,hr): #Taupunkt
    if tc >= 0:
        a = 7.5
        b = 237.3
    elif tc < 0:
        a = 7.6
        b = 240.7
    sdd = 6.1078 * 10**((a*tc)/(b+tc))
    dd = hr/100 * sdd
    v = math.log10(dd/6.1078)
    tp = b*v/(a-v)
    return(round(tp,1))

def getClockARM(): #CPU-Clock
    res = os.popen('vcgencmd measure_clock arm').readline()
    res=res.replace("frequency(48)=","")
    return(int(res)/1E9)


#Function PWM-Out
def K1_SET(angle):
    global K1DC
    K1DC=angle
#    K1W.ChangeDutyCycle(int(angle))

def K2_SET(angle):
    global K2DC
    K2DC=angle
#    K2W.ChangeDutyCycle(int(angle))

def K12_SET(angle):
    global K12DC
    K12DC=angle

def K3_OUT(angle):
    global K3DC
    K3DC=angle
    K3W.ChangeDutyCycle(int(angle))

def K4_OUT(angle):  #PWM else DAC
    global K4DC
    K4DC=angle
#   K4W.ChangeDutyCycle(int(angle)) #PWM
    dac.normalized_value = int(angle)/100 #DAC
    

#Werte sichern
def sichern():
    kanalw=open('./kanal.conf','w')
    kanalw.write(str(K1DC))
    kanalw.write('\r\n')
    kanalw.write(str(K2DC))
    kanalw.write('\r\n')
    kanalw.write(str(K3DC))
    kanalw.write('\r\n')
    kanalw.write(str(K12DC))
    kanalw.write('\r\n')   
    kanalw.close()
    dac.save_to_eeprom() #write init value to eeprom of dac
    
#letzte Werte lesen
kanal=open('./kanal.conf','r+')
K1DC = int(kanal.readline())
K2DC = int(kanal.readline())
K3DC = int(kanal.readline())
K12DC = int(kanal.readline())
K4DC = 0
kanal.close()

GPIO.setmode(GPIO.BCM)
#for i in {K0, K1, K2, K3, K4}: #PWM
for i in {K1, K2, K3}:  #DAC
    GPIO.setup(i, GPIO.OUT)

K1W = GPIO.PWM(K1, FRQ) # K1
K1W.start(K1DC) # Initialisierung

K2W = GPIO.PWM(K2, FRQ) # K2
K2W.start(K2DC) # Initialisierung

K3W = GPIO.PWM(K3, FRQ) # K3
K3W.start(K3DC) # Initialisierung

dac.normalized_value = int(K4DC)/100

#K4W = GPIO.PWM(K4, 150) # K4 #PWM
#K4W.start(K4DC) # Initialisierung  #PWM

#A/D Wandler INIT
GAIN=1
volt=4.096/32767 # bei Gain 1
kr=1.002 #Korrektur für Anzeige
gv="#00FF88" #Messwert gut
bv="#FFFF00" #Messwert schlecht
bbg="#BBBB00"
alarm = False

#GUI Init
from tkinter import *
root = Tk() # Fenster erstellen
root.wm_title("AstroRaspi2 Control") # Fenster Titel
root.config(background = "#440000") # Hintergrundfarbe des Fensters
Ww = 800 # Widgetwith
Wh = 75 # Wigdethight
SCL = 95 # Scalewidth
sw = root.winfo_screenwidth()-Ww-150 #x-Position
sh = root.winfo_screenheight()-Wh-55 #y-Position
# width x height + x_offset + y_offset
root.geometry(str(Ww)+"x"+str(Wh)+"+"+str(sw)+"+"+str(sh)) #Größe und Psoition setzen
fh=12 #Fontgröße
fhl=fh-3
scw=8 #Höhe Schieberegler
t=3000 #alle 3000ms neu messen

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

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

#Wertebeschriftungen Frame 1
lb1_1 = Label(Frame1, text="3V3:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_1.grid(row=0, column=0, padx=3, pady=3)

lb1_2 = Label(Frame1, text="5V0:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_2.grid(row=0, column=2, padx=3, pady=3)

lb1_3 = Label(Frame1, text="Batt:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_3.grid(row=0, column=4, padx=3, pady=3)

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

lb1_5 = Label(Frame1, text="T °C:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_5.grid(row=0, column=8, padx=3, pady=3)

lb1_6 = Label(Frame1, text="Hr %:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_6.grid(row=0, column=10, padx=3, pady=3)

lb1_7 = Label(Frame1, text="Dew °C:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_7.grid(row=0, column=12, padx=3, pady=3)

lb1_7 = Label(Frame1, text="CPU °C:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_7.grid(row=0, column=14, padx=3, pady=3)

lb1_9 = Label(Frame1, text="GHz:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_9.grid(row=0, column=16, padx=3, pady=3)

#Werteausgabe erstellen
lb1_11 = Label(Frame1, text="0.000",font=('arial',fh),bg=lbbg)
lb1_11.grid(row=0, column=1, padx=3, pady=3)

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

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

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

lb1_15 = Label(Frame1, text="000.0",font=('arial',fh),bg=lbbg)
lb1_15.grid(row=0, column=9, padx=3, pady=3)

lb1_16 = Label(Frame1, text="000.0",font=('arial',fh),bg=lbbg)
lb1_16.grid(row=0, column=11, padx=3, pady=3)

lb1_17 = Label(Frame1, text="00.0",font=('arial',fh),bg=lbbg)
lb1_17.grid(row=0, column=13, padx=3, pady=3)

lb1_18 = Label(Frame1, text="00.0",font=('arial',fh),bg=lbbg)
lb1_18.grid(row=0, column=15, padx=3, pady=3)

lb1_19 = Label(Frame1, text="0.0",font=('arial',fh),bg=lbbg)
lb1_19.grid(row=0, column=17, padx=3, pady=3)

#Kanaele beschriften Frame 2
K1L = Label(Frame2, text="K1",font=('arial',fhl),bg=lbbg,fg=lbfg)
K1L.grid(row=0, column=0, padx=3, pady=3)

K1V = Label(Frame2, text="000",font=('arial',fhl),bg=lbbg,fg=lbfg)
K1V.grid(row=0, column=2, padx=3, pady=3)

K2L = Label(Frame2, text="K2",font=('arial',fhl),bg=lbbg,fg=lbfg)
K2L.grid(row=0, column=3, padx=3, pady=3)

K2V = Label(Frame2, text="000",font=('arial',fhl),bg=lbbg,fg=lbfg)
K2V.grid(row=0, column=5, padx=3, pady=3)

K12L = Label(Frame2, text="HO",font=('arial',fhl),bg=lbbg,fg=lbfg) #Humity overdrive
K12L.grid(row=0, column=6, padx=3, pady=3)

K3L = Label(Frame2, text="FN",font=('arial',fhl),bg=lbbg,fg=lbfg)
K3L.grid(row=0, column=8, padx=3, pady=3)

K4L = Label(Frame2, text="FT",font=('arial',fhl),bg=lbbg,fg=lbfg)
K4L.grid(row=0, column=10, padx=3, pady=3)

#Scales
#Kanal1 nur PWM
K1S = Scale(Frame2, from_=0, to=100, resolution=5, orient=HORIZONTAL, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8),command=K1_SET,width=scw)
K1S.set(K1DC)
K1S.grid(row=0, column=1, padx=3, pady=3)
#Kanal2 nur PWM
K2S = Scale(Frame2, from_=0, to=100, resolution=5, orient=HORIZONTAL, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8),command=K2_SET,width=scw)
K2S.set(K2DC)
K2S.grid(row=0, column=4, padx=3, pady=3)

K12S = Scale(Frame2, from_=0, to=100, resolution=5, orient=HORIZONTAL, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8),command=K12_SET,width=scw)
K12S.set(K12DC)
K12S.grid(row=0, column=7, padx=3, pady=3)
#Kanal3 Luefter
K3S = Scale(Frame2, from_=0, to=100, resolution=100, orient=HORIZONTAL, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8),command=K3_OUT,width=scw)
K3S.set(K3DC)
K3S.grid(row=0, column=9, padx=3, pady=3)
#Kanal4 Flat mit LED-Treiber
K4S = Scale(Frame2, from_=0, to=100, resolution=5, orient=HORIZONTAL, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8),command=K4_OUT,width=scw)
K4S.set(K4DC)
K4S.grid(row=0, column=11, padx=3, pady=3)

SiBu=Button(Frame2,text="Save",command=sichern, bg="#770000",fg="#DDDDDD", font=('arial',8))
SiBu.grid(row=0,column=12, padx=3, pady=3)

def rw_io():  #ADS1x15 & SHT31 auslesen
    Alarm = False
    P0 = ads.read_adc(0, gain=GAIN) * volt
    P0fg=gv
    if P0 < 3.14 or P0 > 3.47:#Toleranzen 3,3V
        P0fg=bv
        Alarm =True
    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
        Alarm =True
    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
        Alarm =True
    P3 = ads.read_adc(3, gain=GAIN) * volt
    P3fg=gv
    if P3 < 2 or P3 > 3.47:#Toleranzen GoldCap(RTC)
        P3fg=bv
        Alarm=True
    ads.stop_adc()
    P4 = int(hus.temperature)
    P5 = int(hus.relative_humidity)
    P6 = DewPoint(P4,P5)
    P6fg = gv
    if (P4 - P6) < 3:
        P6fg="#FF0000"
        Alarm = True   
    elif (P4 - P6) <5:
        P6fg=bv
        Alarm=True
    P7=(cpu.temperature)
    P7fg = gv
    if P7 > 60:
        P7fg="#FF0000"
        Alarm=True
    elif P7 > 50:
        P7fg=bv
        Alarm=True
    P8 = getClockARM()
#Werte Ausgeben
    lb1_11.configure(text="{:>5.3f}".format(P0),fg=P0fg,bg=lbbg)
    lb1_12.configure(text="{:>5.3f}".format(P1),fg=P1fg,bg=lbbg)
    lb1_13.configure(text="{:>5.3f}".format(P2),fg=P2fg,bg=lbbg)
    lb1_14.configure(text="{:>5.3f}".format(P3),fg=P3fg,bg=lbbg)
    lb1_15.configure(text="{:>5.1f}".format(P4),fg=gv,bg=lbbg)
    lb1_16.configure(text="{:>5.1f}".format(P5),fg=gv,bg=lbbg)
    lb1_17.configure(text="{:>5.1f}".format(P6),fg=P6fg,bg=lbbg)
    lb1_18.configure(text="{:>1.1f}".format(P7),fg=P7fg,bg=lbbg)
    lb1_19.configure(text="{:>1.1f}".format(P8),fg=gv,bg=lbbg)

    P5 = P5 / 10000 * int(K12DC) * 2/(math.log10(100-int(P5)))#calculate overdrive manual and humidity
    P51 = int(int(K1DC) + int(K1DC) * P5)
    if P51 > 100: P51 = 100
    K1V.configure(text="{:>5.0f}".format(P51)) #out channel 1
    P52 = int(int(K2DC) + int(K2DC) * P5)
    if P52 > 100: P52 = 100
    K2V.configure(text="{:>5.0f}".format(P52)) #out channel 2
    K1W.ChangeDutyCycle(int(P51))
    K2W.ChangeDutyCycle(int(P52))
    if Alarm == True:
        root.attributes("-topmost", True)
    else:
        root.attributes("-topmost", False)
#GUI-Schleife   
    Frame1.after(t, rw_io)
    
rw_io() #Initial Lesen und Ausgeben
    
root.mainloop() # GUI wird upgedated. Danach keine Elemente setzen
 
Hallo!
Um die Helligkeit des LCD-Displays komfortabel in den Griff zu bekommen, habe ich der LCD-Schaltung einen MCP4725 spendiert. Dieser generiert eine weitere Steuerspannung für den LED-Treibertransistor, welche über einen 47k Widerstand (R7) und einer Endkopplungsdiode (D2) zusätzlich angesteuert werden kann.
Die Widerstände für die Grundhelligkeit sind entfallen, diese Einstellung kann nun komfortabel über die Pythonoberfläche erfolgen.
Da schon ein MCP4725 im System steckt auf Adresse 0x60h, ist bei diesem hier der A0 Eingang auf Vcc gelegt, sodass diese auf 0x61h angesprochen werden kann. Leider sind die kleinen DAC auf dem europäischen Markt ausverkauft, sodass ich in China nachordern musste.
 

Anhänge

  • LCD-I2C-203B02.sch.pdf
    15,4 KB · Aufrufe: 96
  • LCD-I2C-203B02.brd.pdf
    34,5 KB · Aufrufe: 93
  • DSC_0040.JPG
    DSC_0040.JPG
    134 KB · Aufrufe: 244
Die Software ist das schon erwähnte Pythonprogramm, welches um die entsprechenden Ansteuerungen der Hard- und Software erweitert wurde. Um den Einsatz auf unterschiedlichen Hardwareständen zu ermöglichen, werden im Vorfeld entsprechenden Adressen auf Vorhandensein eines Devices abgefragt.
Python:
import smbus
i2cb=smbus.SMBus(1)

D60H=False
D61H=False

for device in range(128):
    try:
        i2cb.read_byte(device)
        if device==96: D60H = True #DAC for Flatfield
        if device==97: D61H = True #DAC for Brightness
    except: # exception if read_byte fails
        pass

Ist das dementsprechende Device nicht vorhanden (z.B. Flag D60H bleibt auf False), wird der zugeordnete Regler einfach nicht dargestellt ...
Python:
#Kanal4 Flat mit LED-Treiber
if D60H==True:
    K4S = Scale(Frame2, from_=100, to=0, resolution=5, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8,'bold'),command=K4_OUT,width=scw)
    K4S.set(K4DC)
    K4S.grid(row=0, column=4, padx=3, pady=3)

...und alle Hardwarezugriffe nicht angesprochen. Klapp bisher prima.
Python:
if D60H==True: dac.normalized_value = int(K4DC)/100 #Flatfield

1641324295750.png


Im gezeigten Beispiel fehlt hier bei einem älteren Raspi die Ansteuerung für die Helligkeit, Beschriftung wird noch durchgeführt, der Regler bleibt verborgen.

Hier der Gesamtcode:
Python:
#!/usr/bin/env python3
import os
import board
import busio
import smbus
import RPi.GPIO as GPIO
import adafruit_mcp4725 #DAC
import adafruit_sht31d #Humity

from ADS1x15 import ADS1115 #ADC x4
from gpiozero import CPUTemperature

i2c = busio.I2C(board.SCL, board.SDA) #i2c-bus
i2cb=smbus.SMBus(1)

D60H=False
D61H=False

for device in range(128):
    try:
        i2cb.read_byte(device)
        if device==96: D60H = True
        if device==97: D61H = True #DAC for Brightness
    except: # exception if read_byte fails
        pass


ads = ADS1115() #ADC 4x
hus = adafruit_sht31d.SHT31D(i2c)
if D60H==True: dac = adafruit_mcp4725.MCP4725(i2c,address=0x60) #DAC for Flatfield
if D61H==True: brn = adafruit_mcp4725.MCP4725(i2c,address=0x61) #DAC for Brightness
cpu = CPUTemperature()

#init GPIO
#K0 = 5  #Kanal 0 - GPIO5
K1 = 6  #Kanal 1 - GPIO6
K2 = 13 #Kanal 2 - GPIO13
K3 = 12 #Kanal 3 - GPIO12 / FAN
#K4 = 23 #Kanal 4 - GPIO23 / Flat #PWM
FRQ = 100 #100Hz
Bits=16

def getClockARM():
    res = os.popen('vcgencmd measure_clock arm').readline()
    res=res.replace("frequency(48)=","")
    return(int(res)/1E9)


#Function PWM-Out
def K1_SET(angle):
    global K1DC
    K1DC=angle
#    K1W.ChangeDutyCycle(int(angle))

def K2_SET(angle):
    global K2DC
    K2DC=angle
#    K2W.ChangeDutyCycle(int(angle))

def K12_SET(angle):
    global K12DC
    K12DC=angle

def K3_OUT(angle):
    global K3DC
    K3DC=angle
    K3W.ChangeDutyCycle(int(angle))

def K4_OUT(angle):  #PWM else DAC
    global K4DC
    K4DC=angle
#   K4W.ChangeDutyCycle(int(angle)) #PWM
    if D60H==True: dac.normalized_value = int(angle)/100 #Flatfield

def K5_OUT(angle): #Brightness
    global K5DC
    K5DC=angle
    if D61H==True: brn.normalized_value = int(angle)/100 #Brightness
   

#Werte sichern
def sichern():
    kanalw=open('./kanal.conf','w')
    kanalw.write(str(K1DC))  #K1
    kanalw.write('\r\n')
    kanalw.write(str(K2DC))  #K2
    kanalw.write('\r\n')
    kanalw.write(str(K3DC))  #K3 Flatfield
    kanalw.write('\r\n')
    kanalw.write(str(K12DC)) #K1 & 2 Override
    kanalw.write('\r\n')  
    kanalw.write(str(K4DC))  #K4 FAN
    kanalw.write('\r\n')  
    kanalw.write(str(K5DC))  #K5 Brightness LCD
    kanalw.write('\r\n')  
    kanalw.close()
    if D60H==True: dac.save_to_eeprom() #write init value to eeprom of dac
    if D61H==True: brn.save_to_eeprom() #write init value to eeprom of dac
   
#letzte Werte lesen
kanal=open('./kanal.conf','r+')
K1DC = int(kanal.readline())
K2DC = int(kanal.readline())
K3DC = int(kanal.readline())
K12DC = int(kanal.readline())
K4DC = int(kanal.readline())
K5DC = int(kanal.readline())
kanal.close()

GPIO.setmode(GPIO.BCM)
#for i in {K0, K1, K2, K3, K4}: #PWM
for i in {K1, K2, K3}:  #DAC
    GPIO.setup(i, GPIO.OUT)

K1W = GPIO.PWM(K1, FRQ) # K1
K1W.start(K1DC) # Initialisierung

K2W = GPIO.PWM(K2, FRQ) # K2
K2W.start(K2DC) # Initialisierung

K3W = GPIO.PWM(K3, FRQ) # K3
K3W.start(K3DC) # Initialisierung

if D60H==True: dac.normalized_value = int(K4DC)/100 #Flatfield
if D61H==True: brn.normalized_value = int(K5DC)/100 #Brightness


#K4W = GPIO.PWM(K4, 150) # K4 #PWM
#K4W.start(K4DC) # Initialisierung  #PWM

#A/D Wandler INIT
GAIN=1
volt=4.096/32767 # bei Gain 1
kr=1.002 #Korrektur für Anzeige
gv="#00FF88" #Messwert gut
bv="#FFFF00" #Messwert schlecht
bbg="#BBBB00"
alarm = False

#GUI Init
from tkinter import *
root = Tk() # Fenster erstellen
root.wm_title("AstroRaspi2 Control") # Fenster Titel
root.config(background = "#440000") # Hintergrundfarbe des Fensters
Ww = 500# Widgetbreite
Wh = 110 # Wigdethöhe
SCL = 75 # ScaleWidth
sw = root.winfo_screenwidth()-Ww-150 #x-Position ermitteln
sh = root.winfo_screenheight()-Wh-55 #y-Position ermitteln
# width x height + x_offset + y_offset
root.geometry(str(Ww)+"x"+str(Wh)+"+"+str(sw)+"+"+str(sh)) #Größe und Psoition setzen
fh=12 #Fontgröße
fhl=fh-3
scw=6 #Höhe Schieberegler
t=3000 #alle 3000ms neu messen

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

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

#Wertebeschriftungen Frame 1
lb1_1 = Label(Frame1, text="3V3:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_1.grid(row=0, column=0, padx=3, pady=3)

lb1_2 = Label(Frame1, text="5V0:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_2.grid(row=1, column=0, padx=3, pady=3)

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

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

lb1_5 = Label(Frame1, text="T °C:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_5.grid(row=0, column=2, padx=3, pady=3)

lb1_6 = Label(Frame1, text="Hr %:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_6.grid(row=1, column=2, padx=3, pady=3)

lb1_7 = Label(Frame1, text="CPU °C:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_7.grid(row=2, column=2, padx=3, pady=3)

lb1_8 = Label(Frame1, text="GHz:",font=('arial',fhl),bg=lbbg,fg=lbfg)
lb1_8.grid(row=3, column=2, padx=3, pady=3)

#Werteausgabe erstellen
lb1_11 = Label(Frame1, text="0.000",font=('arial',fh),bg=lbbg)
lb1_11.grid(row=0, column=1, padx=3, pady=3)

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

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

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

lb1_15 = Label(Frame1, text="000.0",font=('arial',fh),bg=lbbg)
lb1_15.grid(row=0, column=3, padx=3, pady=3)

lb1_16 = Label(Frame1, text="000.0",font=('arial',fh),bg=lbbg)
lb1_16.grid(row=1, column=3, padx=3, pady=3)

lb1_17 = Label(Frame1, text="00.0",font=('arial',fh),bg=lbbg)
lb1_17.grid(row=2, column=3, padx=3, pady=3)

lb1_18 = Label(Frame1, text="0.0",font=('arial',fh),bg=lbbg)
lb1_18.grid(row=3, column=3, padx=3, pady=3)

K1V = Label(Frame2, text="000",font=('arial',fhl),bg=lbbg,fg=lbfg)
K1V.grid(row=1, column=0, padx=3, pady=3)

K2V = Label(Frame2, text="000",font=('arial',fhl),bg=lbbg,fg=lbfg)
K2V.grid(row=1, column=1, padx=3, pady=3)

K12L = Label(Frame2, text="Curve",font=('arial',fhl),bg=lbbg,fg=lbfg) #Humity overdrive
K12L.grid(row=1, column=2, padx=3, pady=3)

K3L = Label(Frame2, text="Fan",font=('arial',fhl),bg=lbbg,fg=lbfg)
K3L.grid(row=1, column=3, padx=3, pady=3)

K4L = Label(Frame2, text="Flat",font=('arial',fhl),bg=lbbg,fg=lbfg)
K4L.grid(row=1, column=4, padx=3, pady=3)

K5L = Label(Frame2, text="LCD",font=('arial',fhl),bg=lbbg,fg=lbfg)
K5L.grid(row=1, column=5, padx=3, pady=3)

#Scales
#Kanal1 nur PWM
K1S = Scale(Frame2, from_=100, to=0, resolution=5, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8,'bold'),command=K1_SET,width=scw)
K1S.set(K1DC)
K1S.grid(row=0, column=0, padx=3, pady=3)
#Kanal2 nur PWM
K2S = Scale(Frame2, from_=100, to=0, resolution=5, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8,'bold'),command=K2_SET,width=scw)
K2S.set(K2DC)
K2S.grid(row=0, column=1, padx=3, pady=3)

K12S = Scale(Frame2, from_=100, to=0, resolution=5, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8,'bold'),command=K12_SET,width=scw)
K12S.set(K12DC)
K12S.grid(row=0, column=2, padx=3, pady=3)
#Kanal3 Luefter
K3S = Scale(Frame2, from_=100, to=0, resolution=100, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8,'bold'),command=K3_OUT,width=scw)
K3S.set(K3DC)
K3S.grid(row=0, column=3, padx=3, pady=3)
#Kanal4 Flat mit LED-Treiber
if D60H==True:
    K4S = Scale(Frame2, from_=100, to=0, resolution=5, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8,'bold'),command=K4_OUT,width=scw)
    K4S.set(K4DC)
    K4S.grid(row=0, column=4, padx=3, pady=3)

#Kanal5 Brightness LCD
if D61H==True:
    K5S = Scale(Frame2, from_=50, to=10, resolution=1, length=SCL,bg="#770000",fg="#DDDDDD",font=('arial',8,'bold'),command=K5_OUT,width=scw)
    K5S.set(K5DC)
    K5S.grid(row=0, column=5, padx=3, pady=3)


SiBu=Button(Frame2,text="Save",command=sichern, bg="#770000",fg="#DDDDDD", font=('arial',8),height=5, width=1)
SiBu.grid(row=0,column=6, padx=3, pady=3)

def rw_io():  #ADS1x15 & SHT31 auslesen
    Alarm = False
    P0 = ads.read_adc(0, gain=GAIN) * volt
    P0fg=gv
    if P0 < 3.14 or P0 > 3.47:#Toleranzen 3,3V
        P0fg=bv
        Alarm =True
    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
        Alarm =True
    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
        Alarm =True
    P3 = ads.read_adc(3, gain=GAIN) * volt
    P3fg=gv
    if P3 < 2 or P3 > 3.47:#Toleranzen GoldCap(RTC)
        P3fg=bv
        Alarm=True
    ads.stop_adc()
#   P4 = 20
    P4 = hus.temperature
#   P5 = 36
    P5 = hus.relative_humidity
    P6=(cpu.temperature)
    P6fg = gv
    if P6 > 50:
        P6fg=bv
        Alarm=True
    if P6 > 60:
        P6fg="#FF0000"
        Alarm=True
    P7 = getClockARM()
#Werte Ausgeben
    lb1_11.configure(text="{:>5.3f}".format(P0),fg=P0fg,bg=lbbg)
    lb1_12.configure(text="{:>5.3f}".format(P1),fg=P1fg,bg=lbbg)
    lb1_13.configure(text="{:>5.3f}".format(P2),fg=P2fg,bg=lbbg)
    lb1_14.configure(text="{:>5.3f}".format(P3),fg=P3fg,bg=lbbg)
    lb1_15.configure(text="{:>5.1f}".format(P4),fg=gv,bg=lbbg)
    lb1_16.configure(text="{:>5.1f}".format(P5),fg=gv,bg=lbbg)
    lb1_17.configure(text="{:>5.1f}".format(P6),fg=P6fg,bg=lbbg)
    lb1_18.configure(text="{:>1.1f}".format(P7),fg=gv,bg=lbbg)
   
    P5 = P5 / 10000 * int(K12DC) #calculate overdrive
    P51 = int(int(K1DC) + int(K1DC) * P5)
    if P51 > 100: P51 = 100
    K1V.configure(text="K1{:>5.0f}".format(P51)) #out channel 1
    P52 = int(int(K2DC) + int(K2DC) * P5)
    if P52 > 100: P52 = 100
    K2V.configure(text="K2{:>5.0f}".format(P52)) #out channel 2
    K1W.ChangeDutyCycle(int(P51))
    K2W.ChangeDutyCycle(int(P52))
    if Alarm == True:
        root.attributes("-topmost", True)
    else:
        root.attributes("-topmost", False)
#GUI-Schleife  
    Frame1.after(t, rw_io)
   
rw_io() #Initial Lesen und Ausgeben
   
root.mainloop() # GUI wird upgedated. Danach keine Elemente setzen
 
Hallo,
da das System immer wieder zu neuen Ideen anregt, hier mal ein kleiner Auszug "Python und gpsd"
Wenn man gps "on Board" beim Raspi hat, läuft in der Regel auch der GPS-Deamon. Diesen kann man auch per Python auslesen. Ist allerdings ein wenig tricky, aber es geht mit ein wenig Probieren trotzdem. Als Anregung dient diese Seite, auch dieser Code führt mehr oder weniger zum Erfolg.
Als "stand alone" zum Testen für einen Durchlauf wurde dieser Codeschnipsel.
Python:
import gps               # the gpsd interface module

gpsd = gps.gps(mode=gps.WATCH_ENABLE)

while 0 == gpsd.read():
    if not (gps.MODE_SET & gpsd.valid):
        # not useful, probably not a TPV message
        continue

    print('Mode: %s(%d) Time: ' % (("Invalid", "NO_FIX", "2D", "3D")[gpsd.fix.mode],gpsd.fix.mode), end="")
        # print time, if we have it
    if gps.TIME_SET:
        print(gpsd.fix.time)
    else:
        print('n/a')

    if ((gps.isfinite(gpsd.fix.latitude) and gps.isfinite(gpsd.fix.longitude))):
        print(" Lat %.4f Lon %.4f" % (gpsd.fix.latitude, gpsd.fix.longitude))
    else:
        print(" Lat n/a Lon n/a")
    if (gps.isfinite(gpsd.fix.altitude)):
        print(" Alt %.3f" % (gpsd.fix.altitude))
    else:
        print(" Alt n/a")
    break

Für die Integration in das große Steuerscript wurde dies hier daraus:
Python:
import gps               # the gpsd interface module

gpsd = gps.gps(mode=gps.WATCH_ENABLE)

while 0 == gpsd.read():
    if not (gps.MODE_SET & gpsd.valid):
        # not useful, probably not a TPV message
        continue

    gMode=('Mode: %s(%d) Time: ' % (("Invalid", "NO_FIX", "2D", "3D")[gpsd.fix.mode],gpsd.fix.mode))
    if gps.TIME_SET:
        gTime="Time: " + str(gpsd.fix.time)
    else:
        gTime=('n/a')

    if ((gps.isfinite(gpsd.fix.latitude) and gps.isfinite(gpsd.fix.longitude))):
        gLoc=(" Lat %.4f Lon %.4f" % (gpsd.fix.latitude, gpsd.fix.longitude))
    else:
        gLoc=(" Lat n/a Lon n/a")
    if (gps.isfinite(gpsd.fix.altitude)):
        gAlt=(" Alt %.3f" % (gpsd.fix.altitude))
    else:
        gAlt=(" Alt n/a")
    break
Hier werden 4 variablen mit Werten belegt zur Übergabe an die Grafische Ausgabe
Die sieht dann so aus:
1663696552763.png
 
Zuletzt bearbeitet:
Hallo!
Um die GPS-Koordinaten nicht in Dezimalgrad sondern richtig schön in Grad - Minute -Sekunde ausgeben möchte mus die Zeile
gLoc=("Lat %.6f \t Lon %.6f" % (gpsd.fix.latitude, gpsd.fix.longitude))
durch diese 3 Zeilen ersetzen
gLat=float("%.6f" % (gpsd.fix.latitude)) gLon=float("%.6f" % (gpsd.fix.longitude)) gLoc=(f"Lat: {int(gLat)}°{int(abs(gLat) % 1 * 60)}'{abs(gLat * 60) % 1 * 60:.3f}'' ") + (f"Lon: {int(gLon)}°{int(abs(gLon) % 1 * 60)}'{abs(gLon * 60) % 1 * 60:.3f}''")
Sieht dann so aus (Idee ist von hier):
1664820540944.png

Einen Haken hat allerdings die Abfrage:
while 0 == gpsd.read(): if not (gps.MODE_SET & gpsd.valid): # not useful, probably not a TPV message continue

P.S. Hängt sich mittendrin die Verbindung zum GPS Deamon auf (Ausfall Bluetoothverbindung etc.), hängt auch das Script in einer Endlosschleife fest. Ich muss mal schauen, wie ich das gelöst bekomme.
 
Hallo!
Um bei einer Neuinstallation nicht immer von Hand die Initialisierung der ~/bin/kanal.conf manuell zu erledigen, ist dieses Stück Code entstanden.

Python:
#letzte Werte lesen
if os.path.isfile(os.environ["HOME"]+'/bin/kanal.conf') == True: #kanal.conf exist?
    kanal=open(os.environ["HOME"]+'/bin/kanal.conf','r+')
    K1DC = int(kanal.readline())
    K2DC = int(kanal.readline())
    K3DC = int(kanal.readline())
    K12DC = int(kanal.readline())
    K4DC = int(kanal.readline())
    K5DC = int(kanal.readline())
    kanal.close()
else:
    open(os.environ["HOME"]+'/bin/kanal.conf','w').close() #no create new with standart values
    K1DC=20
    K2DC=20
    K3DC=0
    K12DC=50
    K4DC=0
    K5DC=30
    sichern()

Einfach diesen Hier mit obigen ersetzen.
Python:
#letzte Werte lesen
kanal=open('./kanal.conf','r+')
K1DC = int(kanal.readline())
K2DC = int(kanal.readline())
K3DC = int(kanal.readline())
K12DC = int(kanal.readline())
K4DC = int(kanal.readline())
K5DC = int(kanal.readline())
 
N'Abend!

Damit ich bei den "vielen" Raspi's noch durchblicke, kann man die WLAN-MAC Adresse abfragen und somit sein Gerät "human readable" ausgeben lassen, auf welchen man gerade was bearbeiten will.

Python:
wmac = (os.popen('cat /sys/class/net/wlan0/address').readline()).strip() #detect the Device
DevRasp="unknown"
if wmac=="xx:xx:xx:xx:xx:00": DevRasp="Testraspi"
elif wmac=="xx:xx:xx:xx:xx:01": DevRasp="AstroRaspiV2-1"
 
Hallo!
Durch meine bessere Hälfte bin ich auf die Idee des Loggen gebracht worden. Das hin und wieder mal vorkommt, dass irgendwie Abstürze in der Nacht doch auftauchen, wäre es nicht Schlecht, dass Ganze da auch nachvollziehen zu können. Also wurde das Pyhton Script zur Steuerung und Überwachung angepasst.
Hilfreich dazu ist auch diese Seite.

Zur Initialisierung kann man folgenden Code nehmen:
Python:
import logging

logging.basicConfig(
    filename='arpi5c.log',
    filemode='w',
    format='%(asctime)s %(levelname)s: %(message)s',
    datefmt='%d.%m.%y %H:%M:%S',
    level=logging.DEBUG
)

Gratis gibt es dann auch noch Python Fehler dazu, wenn sie denn auftreten.
Ich lasse dazu als Info alle relevanten Variablen (Spannungen, Temperatur, Zustand GPS ...) mit loggen.
Dieser Befehl mit Formatierungen:
Python:
#logging
    logging.info("3V3:{:>5.3f}".format(P0) + " 5V:{:>5.3f}".format(P1) + " 12V:{:>5.3f}".format(P2) + " RTC:{:>5.3f}".format(P3) +
                 " T:{:>4.1f}".format(P4) + " Hr:{:>3.1f}".format(P5) + " CPU:{:>3.1f}".format(P6) + " GHz:{:>3.1f}".format(P7) +
                 " DP:{:>4.1f}".format(P8) + " GPS:"f'{gMode}'+ f'{gLoc}' + f'{gAlt}')

Bringt dieses LogFile zustande ...
Code:
29.04.23 15:54:33 INFO: 3V3:3.294 5V:4.948 12V:11.954 RTC:3.292 T:21.5 Hr:48.0 CPU:40.9 GHz:1.9 DP:10.0 GPS:3D(3) Lat: 50°32'5.280''   Lon: 10°24'41.087''   Alt 297.420  /dev/rfcomm1
29.04.23 15:54:36 INFO: 3V3:3.294 5V:4.948 12V:11.954 RTC:3.292 T:21.5 Hr:47.9 CPU:39.4 GHz:1.9 DP:10.0 GPS:3D(3) Lat: 50°32'5.276''   Lon: 10°24'41.069''   Alt 297.157  /dev/rfcomm1
29.04.23 15:54:39 INFO: 3V3:3.294 5V:4.948 12V:11.963 RTC:3.292 T:21.6 Hr:47.9 CPU:39.9 GHz:1.9 DP:10.1 GPS:3D(3) Lat: 50°32'5.276''   Lon: 10°24'41.062''   Alt 297.027  /dev/rfcomm1
29.04.23 15:54:42 INFO: 3V3:3.294 5V:4.948 12V:11.963 RTC:3.292 T:21.6 Hr:47.8 CPU:38.9 GHz:0.8 DP:10.1 GPS:3D(3) Lat: 50°32'5.269''   Lon: 10°24'41.033''   Alt 296.594  /dev/rfcomm1
29.04.23 15:54:45 INFO: 3V3:3.294 5V:4.948 12V:11.944 RTC:3.292 T:21.6 Hr:48.0 CPU:39.4 GHz:1.9 DP:10.1 GPS:3D(3) Lat: 50°32'5.258''   Lon: 10°24'41.011''   Alt 296.094  /dev/rfcomm1
29.04.23 15:54:48 INFO: 3V3:3.294 5V:4.948 12V:11.963 RTC:3.292 T:21.5 Hr:48.0 CPU:38.5 GHz:0.8 DP:10.1 GPS:3D(3) Lat: 50°32'5.215''   Lon: 10°24'40.993''   Alt 294.914  /dev/rfcomm1
29.04.23 15:54:51 INFO: 3V3:3.294 5V:4.944 12V:11.963 RTC:3.292 T:21.6 Hr:47.9 CPU:38.0 GHz:1.9 DP:10.1 GPS:3D(3) Lat: 50°32'5.219''   Lon: 10°24'40.993''   Alt 294.983  /dev/rfcomm1
29.04.23 15:54:54 INFO: 3V3:3.294 5V:4.948 12V:11.963 RTC:3.292 T:21.6 Hr:47.8 CPU:38.9 GHz:0.7 DP:10.1 GPS:3D(3) Lat: 50°32'5.208''   Lon: 10°24'40.964''   Alt 294.435  /dev/rfcomm1

Ich tüftele noch an der Abfrage des Status der Bluetoothverbindungen, da diese immer sehr sensibel sind bei mir.
 
Die Statusverbindungen der Bluetoothgeräte kann man so abfragen und entsprechend formatieren:
Python:
    BGPS = os.popen('bluetoothctl info 00:08:1B:11:5D:F0 | grep Connected').readline()
    BGPS = BGPS.replace("Connected: ","BGPS:")
    BGPS = BGPS.strip("\t\r\n")

Allerdings durch die "Brust in's Auge", ich hatte mir von der BT-Python Bibliothek mehr erwartet, also geht's wieder in die Shell Ebene.
 
Hallo!
Heute ist es geschehen. Der 4er Pi geht in Rente, der 5-er ist im ersten Gehäuse verbaut worden. Aktuell gibt es keine Einbußen, welche gravierend sind. Der Umbau war leichter als gedacht, da die Maße der beiden inkl. Kühler sich stark ähneln. Der 5-er werkelt wie angedacht mit der 2242 SSD, die Temperaturen halten sich in Grenzen. Die CPU pendelt mit geregelten Lüfter so um die 40°C, die SSD langweilt sich bei ~25°C in der Bude.
1710535246365.png


Fazit: einfach genial.

P.S. nur das Thema mit dem Autoconnect und rfcomm läuft noch nicht. Am in den nächsten Tagen will ich dann mal eine Trockenübung starten.
 
Oben