by Stefan·Kommentare deaktiviert für Regelung – mal schnell mit QML
Hans M. und ich ham vor über 8 Jahren mal eine Regelung für sein Ferienhaus gebaut: https://stefan.box2code.de/2018/05/11/warm-wasser-steuerung/ Neulich kamen wir mal wieder auf das Thema und ham entschieden dass wir das heute viel einfacher, besser und vorallem schneller könnten. Tatsächlich ham wir bis jetzt nur 2 Wochenenden dafür gebraucht
Alles in Allem besteht die ganze Anwendung aus unter 500 Zeilen einfachen QML-Code der unglaublich primitiv und daher wahrscheinlich auch gut zu warten ist. Der C++ – Code enthält nur ein paar Treiber, mit denen man aus dem QML-Code heraus leicht die Hardware ansprechen kann.
Die Applikation ist noch nicht ganz fertig (es fehlt noch die eigentliche Regelschleife), da sich ein Großteil der Hardware grad noch in Italien befindet. Ich schreib hier wenn gar fertig und mit Hardware getestet
Natürlich lassen sich die Parameter der Regelung leicht über TouchPad ändern und beiben nach Neustart erhalten:
Du kannst dir das Repo klonen und erstmal einfach im QtCreator auf deinem PC ausführen. Etwas interessanter ist aber wie man das Ding auf den Linux-Controller bekommt und wie man am geschicktesten die Hardware anbindet
Die Hardware
RPI-Zero 2W: weil der schön klein und billig is 🙂
HDMI-TS-Display 7″ – bekommt man für um die 40€ – über HDMI und USB angeschlossen
Einen MAX31865 – mit dem kann man einfache Widerstands-Temperatursensoren (PT-100) auslesen – wir brauchen 4 Sensoren aber nur einen MAX31865 da wir die Sensoren einfach über Relais multiplexen
Eine 8-Fach Relaiskarte – bekommt man für unter 10€
Einen PCF8574 – mit dem bekommen wir die Relaiskarte angesteuert
Einen Flusssensor, der impulse liefert wenn Wasser durchfließt
Alles in allem unter 100€ Material. Das Gehäuse ist einfach mit dem 3D-Drucker gedruckt.
Befestigt ist das Ganze mit etwas Bauschaum. Wenn du dich fragst warum Bauschaum: Wir hatten auf Arbeit mal bei einem Prototypen ein Display mit Heißkleber fixiert. Leider stand das ganze dann mal für eine Weile in der Hitze von Miami und viel auseinander 🙁 Deshalb auf keinen Fall mehr Heißkleber 🙂
Wir benutzen am RPI nur wenige Pins:
GPIO23 als Eingang für den Flusssensor (auf Tastaturtaste F12 gemapped)
SDA und SCL (GPIO2 & 3) zum Ansteuern der Relaiskarte über PCF8574 (da man mehrere Bausteine an einem Bus betreiben kann könnten wir damit sogar bis zu 64 Relais schalten)
MOSI, MISO, SCK, CS (GPIO8-11) zum Auslesen der PT-100 Sensoren über MAX31865
Normalerweise bau ich mir fast immer ein eigenes Linux-Image mit buildroot. Da hier aber Einfachheit zählt und die Bootzeit ziemlich egal ist, nehmen wir einfach ein fertiges Raspberry Pi OS Lite
Linux Image vorbereiten
Am einfachsten geht das über den Raspberry Pi Imager. Sorge beim Flashen nur dafür, dass SSH aktiviert und mit deinem WLAN verbunden wird.
Läuft dein PI, dann verbinde dich über SSH und installiere mit sudo apt install qt6-*{dev}* cmake Alles was wir so brauchen.
Ändere deine /boot/firmware/config.txt auf folgenden Inhalt
dtparam=spi=on
dtparam=i2c_arm=on
dtoverlay=vc4-kms-v3d
dtoverlay=gpio-key,gpio=23,active_low=1,gpio_pull=up,keycode=88 # mapped to F12
Danach ist ein Reboot angebracht
Kopiere den Quellcode in dein Heimatverzeichnis auf dem PI (mit scp, FileZilla, WinSCP, o.ä.) Mache dir ein Verzeichnis build in deinem Heimatverzeichnis auf dem PI und führe aus dem Verzeichnis heraus cmake ../wwc2; make aus.
Jetzt kannst du die Applikation mit ~/build/appwwc2 -platform eglfs starten
Damit das beim starten automatisch passiert, kannst du den Befehl an deine ~/.bashrc am Ende anhängen
Aktiviere noch schnell über sudo raspi-config den Auto-Login
Wenn du jetzt neu startest, sollte nach dem Booten die Applikation laufen
(optional) Image wartbar machen
Damit du auch außerhalb deines WLANs auf deinen PI zugreifen kannst, packst du in deine /etc/profile die Zeile nmcli device wifi hotspot ssid wwc2 password 12345678 so macht dein PI einen WLAN-Hotspot auf. SSH funktioniert dann über die Adresse 10.42.0.1
by Stefan·Kommentare deaktiviert für ESP32-Breakout
Hans und ich ham uns mal wieder was überlegt. Wir wollten uns ein Board bauen, mit dem wir unsere Standardhardware ohne Fädelaufbauten leicht ansteuern können
Jedes mal herauszusuchen, welche ESP32 Pins für was geeignet sind ist auf Dauer etwas nervig und Fädelaufbauten sind sehr fehleranfällig. Wir wollten daher einfach eine solide Plattform für Alles Mögliche.
Wir wollten:
Vernünftige JTAG-Schnittstelle für Debugging mit dem ESP-Prog aus VSCode heraus (mit PlatformIO)
I2C-Schnittstelle die direkt für MCP23017, PCF8574 (beides Portexpander) für DotMatrixDisplays und unsere RTC passt
SPI-Schnittstele die auf einen SD-Karten-Adapter oder ein TF-Display passt
Verstärkte Versorgung für NeoPixel, damit wir auch große Matrixen ansteuern können
Passende Stecker findet man z.B. hier: https://www.amazon.de/QUARKZMAN-Weiblich-Breadboard-Flachbandkabel-mehrfarbig/dp/B0CTKJL1M1
Dafür ham wir uns ein kleines BreakOut-Board für die ESP32-NodeMCU (https://www.amazon.de/gp/product/B0D9BTQRYT) entworfen (weil die schon Stecker hat und sich einfach auflöten lässt)
Unser Board:
Und unser Schaltplan:
Gerber-Dateien kommen, wenn wir uns sicher sind, das Alles funktioniert.
Teststatus: SPI => OK I2C => OK (Achtung DotMatrix-Display braucht 5V – ham wir ned wirklich berücksichtigt) JTAG => OK Probleme: Layout für NodeMCU ist etwas zu schmal – man muss ganz schön quetschen dass man das Modul nei bekommt 🙁
by Stefan·Kommentare deaktiviert für Signalverarbeitung mit (Micro)python
Ich möcht in diesem Beitrag zeigen, dass man Signalerarbeitung auch ohne viel Theorie und Mathematik hinbekommt und hab dafür eine Bibliothek geschrieben, die klein, supereinfach zu verwenden und vorallem auf einem ESP mit Micropython ihren Dienst tuen kann.
by Stefan·Kommentare deaktiviert für BLE Sensoren mit Micropython (Daum Ergometer als Sensor)
Mein Vermieter Hans fragte mich neulich ob man irgendwie die Daten seines Ergometers (ein altes Daum ergo_bike) in seinen neuen Fahrradcomputer ( Einen Garmin Edge 820 ) bekommen kann, da er in seinem Fahrradcomputer gerne seine gesamten Fitnessdaten für Rennrad und Ergometer hätte
Bei Daum bekommt man für das ergo_bike eine schöne Schnittstellenbeschreibung:
Man kann erkennen, dass die sog. Run_Daten alles enthalten was man so braucht – Leistung, Trittfrequenz, Geschwindigkeit und Puls
Auf der Rückseite des Cockpits findet man den zugehörigen Stecker (oben):
Der Stecker sieht zwar wie RS-232 aus, hat aber etwas andere Pegel (Daum verwendet statt max232 2 Optokoppler für RX und TX) – Deswegen ein PullUp an pin 2 des MAX232.
GND und VCC hab ich der Übersichtlichkeit halber nicht eingezeichnet (und natürlich weil ich faul bin 🙂 )
Das war schon die Hardware – du wirst dich wundern warum 2 ESP32. Leider hat der Fahrradcomputer mehrere Sensoren pro Gerät nicht verstanden und es ging nicht anders 🙁
Nach etwas Micropython hats dann funktioniert:
Code Power-Sensor ( für Trittfrequenz, Leistung und Kadenz )
(oberer ESP32)
Tut mir leid, ist etwas schnell und schlampig geschrieben aber erfüllt seinen Zweck ganz gut
Das Protokoll zwischen Fahrradcomputern und Sensoren ist am besten beschrieben unter https://www.bluetooth.com/specifications/specs/gatt-specification-supplement-5/ und natürlich viele viele andere Typen von Sensoren (z.B. Temperatur, Luftfeuchte …) man findet für fast Alles was. Vielleicht kannst du hier was brauchen für dein Sensorprojekt – es ist sehr leicht BLE-Sensoren zu implementieren ( besonders mit Micropython, finde ich 🙂 )
import bluetooth
import random
import struct
import time
from ble_advertising import advertising_payload
from micropython import const
from machine import UART
uart = UART(2, 9600)
uart.init(9600, bits=8, parity=None, stop=1)
_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_INDICATE_DONE = const(20)
_FLAG_READ = const(0x0002)
_FLAG_NOTIFY = const(0x0010)
_FLAG_INDICATE = const(0x0020)
_UUID_POWER = bluetooth.UUID(0x1818)
_POWER_CHAR = (
bluetooth.UUID(0x2A63),
_FLAG_READ | _FLAG_NOTIFY | _FLAG_INDICATE,
)
_POWER_FEAT_CHAR = (
bluetooth.UUID(0x2A65),
_FLAG_READ | _FLAG_NOTIFY | _FLAG_INDICATE,
)
_POWER_LOC_CHAR = (
bluetooth.UUID(0x2A5D),
_FLAG_READ | _FLAG_NOTIFY,
)
_SERVICE_POWER = (
_UUID_POWER,
(_POWER_CHAR,_POWER_FEAT_CHAR,_POWER_LOC_CHAR,),
)
SERVICES = (_SERVICE_POWER,)
class BLEErgo:
def __init__(self, ble, name="HansErgoPower"):
self._ble = ble
self._ble.active(True)
self._ble.irq(self._irq)
((self._handlePower, self._handlePowerFeat,self._handlePowerLoc,),) = self._ble.gatts_register_services(SERVICES)
self._connections = set()
self._payload = advertising_payload(
name=name, services=[_UUID_POWER]
)
self._advertise()
self.lastCrankRev = 0
self.lastWheelRev = 0
self.lastCrankEvent = 0
self.lastWheelEvent = 0
dat = bytearray()
dat.append(0x0C) # speed and cadence
dat.append(0x00)
dat.append(0x00)
dat.append(0x00)
self._ble.gatts_write(self._handlePowerFeat, dat)
dat = bytearray()
dat.append(6) # right crank
self._ble.gatts_write(self._handlePowerLoc, dat)
def _irq(self, event, data):
if event == _IRQ_CENTRAL_CONNECT:
conn_handle, _, _ = data
self._connections.add(conn_handle)
elif event == _IRQ_CENTRAL_DISCONNECT:
conn_handle, _, _ = data
self._connections.remove(conn_handle)
self._advertise()
elif event == _IRQ_GATTS_INDICATE_DONE:
conn_handle, value_handle, status = data
def setPower(self, pwr, rpm, speed, notify=False, indicate=False):
self.lastWheelRev += 1
self.lastCrankRev += 1
self.lastCrankEvent += int(60 / rpm * 1024)
rnd = int(15500 / speed)
self.lastWheelEvent += rnd
print(rnd)
data = bytearray()
data.append(0x30) # wheel and crank rev present
data.append(0x00)
data+= ((int(pwr)).to_bytes(2, 'little'))
data+= ((int(self.lastWheelRev)).to_bytes(4, 'little'))
data+= ((int(self.lastWheelEvent)).to_bytes(2, 'little'))
data+= ((int(self.lastCrankRev)).to_bytes(2, 'little'))
data+= ((int(self.lastCrankEvent)).to_bytes(2, 'little'))
data.append(0x00)
data.append(0x00)
data.append(0x00)
data.append(0x00)
data.append(0x00)
self._ble.gatts_write(self._handlePower, data)
if notify or indicate:
for conn_handle in self._connections:
if notify:
self._ble.gatts_notify(conn_handle, self._handlePower)
if indicate:
self._ble.gatts_indicate(conn_handle, self._handlePower)
def _advertise(self, interval_us=100000):
self._ble.gap_advertise(interval_us, adv_data=self._payload)
def app():
ble = bluetooth.BLE()
ergo = BLEErgo(ble)
while True:
dat = bytearray()
dat.append(0x40)
dat.append(0x00)
uart.write(dat)
time.sleep_ms(10)
dat = bytearray()
while uart.any():
dat += uart.read()
if len(dat) > 14:
speed = dat[7]
cadence = dat[6]
power = dat[5] * 5
pulse = dat[14]
if cadence == 0:
cadence = 1
if speed == 0:
speed = 1
print ("Leistung: " + str(power))
print ("Geschwindigkeit: " + str(speed))
print ("Kadenz: " + str(cadence))
ergo.setPower(power, cadence, speed, notify=True, indicate=False)
else:
ergo.setPower(42, 42, 42, notify=True, indicate=False)
time.sleep_ms(1000)
if __name__ == "__main__":
app()
Code Speed-Sensor:
(unterer ESP32)
import bluetooth
import random
import struct
import time
from ble_advertising import advertising_payload
from micropython import const
from machine import UART
uart = UART(2, 9600)
uart.init(9600, bits=8, parity=None, stop=1)
_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_INDICATE_DONE = const(20)
_FLAG_WRITE = const(0x0001)
_FLAG_READ = const(0x0002)
_FLAG_NOTIFY = const(0x0010)
_FLAG_INDICATE = const(0x0020)
_UUID_SPEED = bluetooth.UUID(0x1816)
_SPEED_CHAR = (
bluetooth.UUID(0x2A5B),
_FLAG_NOTIFY,
)
_SPEED_FEAT_CHAR = (
bluetooth.UUID(0x2A5C),
_FLAG_READ,
)
_SPEED_LOC_CHAR = (
bluetooth.UUID(0x2A5D),
_FLAG_READ,
)
_SPEED_CONTROL_CHAR = (
bluetooth.UUID(0x2A55),
_FLAG_WRITE | _FLAG_INDICATE,
)
_SPEED_CONTROL_CHAR = (
bluetooth.UUID(0x2A55),
_FLAG_WRITE | _FLAG_INDICATE,
)
_SERVICE_SPEED = (
_UUID_SPEED,
(_SPEED_CHAR,_SPEED_FEAT_CHAR,_SPEED_LOC_CHAR,_SPEED_CONTROL_CHAR,),
)
SERVICES = (_SERVICE_SPEED,)
class BLEErgo:
def __init__(self, ble, name="HansErgoSpeed"):
self._ble = ble
self._ble.active(True)
self._ble.irq(self._irq)
((self._handleSpeed,self._handleSpeedFeat,self._handleSpeedLoc,self._handleSpeedControl,),) = self._ble.gatts_register_services(SERVICES)
self._connections = set()
self._payload = advertising_payload(
name=name, services=[_UUID_SPEED]
)
self._advertise()
self.lastWheelEvent = 0
self.lastWheelRev = 0
dat = bytearray()
dat.append(0x01) # speed
dat.append(0x00)
self._ble.gatts_write(self._handleSpeedFeat, dat)
dat = bytearray()
dat.append(12) # rear wheel
self._ble.gatts_write(self._handleSpeedLoc, dat)
dat = bytearray()
dat.append(0x02)
dat.append(0x00)
self._ble.gatts_write(self._handleSpeedControl, dat)
def _irq(self, event, data):
if event == _IRQ_CENTRAL_CONNECT:
conn_handle, _, _ = data
self._connections.add(conn_handle)
elif event == _IRQ_CENTRAL_DISCONNECT:
conn_handle, _, _ = data
self._connections.remove(conn_handle)
self._advertise()
elif event == _IRQ_GATTS_INDICATE_DONE:
conn_handle, value_handle, status = data
def setSpeed(self, speed, notify=False, indicate=False):
self.lastWheelRev += 1
self.lastWheelEvent += int(15500 / speed / 2)
data = bytearray()
data.append(0x01) # wheel rev present
data+= ((int(self.lastWheelRev)).to_bytes(4, 'little'))
data+= ((int(self.lastWheelEvent)).to_bytes(2, 'little'))
self._ble.gatts_write(self._handleSpeed, data)
if notify or indicate:
for conn_handle in self._connections:
if notify:
self._ble.gatts_notify(conn_handle, self._handleSpeed)
if indicate:
self._ble.gatts_indicate(conn_handle, self._handleSpeed)
def _advertise(self, interval_us=100000):
self._ble.gap_advertise(interval_us, adv_data=self._payload)
def app():
ble = bluetooth.BLE()
ergo = BLEErgo(ble)
dat = bytearray()
while True:
while uart.any():
dat += uart.read()
if len(dat) > 14:
speed = dat[7]
cadence = dat[6]
power = dat[5] * 5
pulse = dat[14]
if cadence == 0:
cadence = 1
if speed == 0:
speed = 1
dat = bytearray()
print ("Geschwindigkeit: " + str(speed))
ergo.setSpeed(speed, notify=True, indicate=False)
time.sleep_ms(100)
else:
#ergo.setSpeed(42, notify=True, indicate=False)
time.sleep_ms(1000)
if __name__ == "__main__":
app()
Ich hab mir vor einiger Zeit ein HANMATEK HO52S Taschenoszi gekauft und bin total begeistert von dem Gerät. Analogbandbreite von 50 MHz, 2 Kanäle und einen integrierten Funktionsgenerator – und das ganze für unter 200 €
Das einzige von dem ich nicht begeistert war, war die PC-Software dazu. Leider nur Windows und unter VirtualBox gings leider auch ned 🙁 Hab mir die Anleitung angeschaut und gesehen, dass man damit eh nicht sehr viel machen kann außer Daten vom Oszi runterzuladen. Eine wirkliche Bedienung des DSOs wäre damit garnicht möglich gewesen.
Hab mir das Gerät etwas genauer angeschaut und wollte rausfinden ob es irgendeine Möglichkeit gibt es auch über den PC anzusprechen. Nach etwas Google hab ich schnell herausgefunden, dass die Firmware des Geräts eigentlich von Owon kommt. Für einige Geräte von Owon gibt es eine Schnittstellendoku im Netz.
Zu meiner großen Freude benutzt das Gerät das supereinfache SCPI-Protokoll über USB – Und ich hatte schon ein Ziel für meinen Urlaub 🙂
Das Bild indem die Kurven dargestellt werden, hab ich einfach vom Oszi selber – Screenshot gemacht, 3x vergrößert und alle Werte wegradiert 🙂 Dann einfach die Werte von der Schnittstelle drüberzeichnen.
Die Software müsste auch für andere ähnliche DSOs von Owon und OEMs funktionieren. Ich würde mich freuen von dir zu hören, wenn du ein anderes Gerät erfolgreich damit zum Laufen bekommst.
Im Archiv findest du ein AppImage, dass du einfach ausführen kannst. Leider musst du noch (als root) install.sh ausführen. Das fügt eine udev-Rule hinzu ohne die eine kommunikation mit dem DSO nicht möglich ist (zumindest als nicht-root).
Die Windows-Benutzer müssen sich noch etwas gedulden. Ich brauche jemand mit einem Windows-Rechner zum Testen, da es leider unter VirtualBox nicht geht (die SW vom Hersteller aber auch nicht). Natürlich würde ich mich riesig freuen, wenn ein/e Windows-Nutzer/in mit DSO dies liest und sich zur Verfügung stellt 🙂
by Stefan·Kommentare deaktiviert für Kundenprojekt PCD ( Polarization-Check-Device )
Codename
PCD ( Polarization-Check-Device )
Kunde:
Führender Hersteller von Test und Messequipment
Projektzeitraum:
etwa 6 Monate
Produktive Stunden:
etwa 20
Tarif:
Regulär
Einsatzzweck:
Polarisationsprüfung von Batteriegebinden für den Rack-Einbau
Quellcode:
Diesmal nix 🙂
Neu erworbene Skills:
In EasyEDA mittlerweile sehr geübt – Neu dazu Generierung von Bestückungsdaten
Leiterplatten fertigen und gleich bestücken lassen ist echt bezahlbar (JLCPCB ist der absolute Hammer !!!)
Rückblick:
Es war ein supercooles Projekt. Ansprechpartner beim Kunden war mein Kumpel Ferdi. Wir hatten einen riesen Spaß und natürlich viele Biere miteinander 🙂
Mal wieder ein richtiges Unsinnsprojekt 🙂 Ein Nudelkocher mit App- bzw. PC-Steuerung über Bluetooth Noch nicht ganz fertig – aber mal die ersten Bilder
by Stefan·Kommentare deaktiviert für Neuer Mikrocontroller
Lange Zeit hab ich mich bei Mikrocontrollern auf 8-Bit beschränkt (8051 und AVR).
Ich dachte immer die 32-Bit ARM Controller wären zu teuer und für die meisten Anwendungen sowieso überdimensioniert.
Aber das stimmt bei weitem nicht mehr…
Gemeinsames Projekt mit Bernd. Ziel ist ein Retro Spielautomat für alte Arcade Klassiker mit dem RaspberryPI.
Bernd ist gerade dabei ein Gehäuse zu bauen und ich hab mich derweil um ein einfaches Menü und
eine Möglichkeit zum Anschluss eines klassischen Joysticks an den PI gekümmert