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 SketchIt3D – 3D Konstruktion ganz einfach
Noch nicht ganz fertig aber schon mal eine kleine Vorschau
Zielsetzung:
Einfach zu erlernen – die meisten konnten innerhalb von 15 Minuten loskonstruieren
Einfach erweiterbar – die Shape-Bibliothek besteht aus einfachen lua-Skripten die sich super mit KI-Hilfe generieren lassen. Werde euch ne kleine Webseite aufsetzen auf der ihr eure Erweiterungen teilen könnt
Interoperabel – manche Sachen (z.B. Kugellager) lassen sich leichter mit OpenSCAD konstruieren. Du kannst diese einfach importieren und in SketchIt3D weiterbearbeiten
Kostenlos und im Quellcode verfügbar (unter der LGPL)
Multi-Plattform dank QT ( fürs erste Linux und Windows )
Du bist mutig und willst ausprobieren ? – Aber Vorsicht ist noch experimentell !!!
https://files.box2code.de/ Benutzername: SketchIt3D Passwort: test Tut mir leid derzeit ist die Windows Version noch aktueller (hab zu wenige Linux Tester – hast du vielleicht Lust dazu ?) Kurzes Video zu älterer Version is auch dabei
Eine brauchbare Software entsteht nicht ohne die Hilfe von netten Leuten die einem gute Ratschläge geben oder sogar richtig eintauchen und neue Versionen ausgiebig testen Vielen Dank an Ferdi, Hans, Eva, Christian, Michi, Irek, Arvid, Tim, Klaus und Gerald
Eine Bitte hätte ich. Wir erleben auf Arbeit gerade eine große Flaute in Form von fehlenden Aufträgen. Es wäre schön, wenn du jemanden kennst der einen guten Entwicklungsdienstleister sucht, an uns weiterleitest. Wir sind https://www.corscience.com und sind Experten für MedTech – würden uns aber auch über Aufträge aus anderen Bereichen freuen
Die erweiterbare Shape-Bibliothek
Die Texturen die du siehst kannst du auch selbst erweitern. Leg einfach JPG-Dateien in den Ordner ~/SketchIt3D/OwnMaterials
Parameter für den ausgewählten Shape
Import aus OpenSCAD und das View-Menü
Objekte im Raum bewegen (geht auch über Tastatur-Shortcuts)
Objekte aneinander ausrichten
Lua-Code für Shapes inspizieren
Und ne kleine Hilfe
Gruppieren dauert manchmal etwas länger (es wird durch CGAL neue Geometrie berechnet)
Export für 3D-Drucker (hier im Anycubic Slicer)
Was fehlt noch ?
Hilfe fehlt noch komplett
Alignment Menü ist noch nicht vollständig und hat noch Fehler
Undo und Redo fehlt noch
Ich möchte noch einen Export für den Mehrfarbendruck einbauen
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 C++ Statemachine-Framework
StateMachines sind das Brot und Butter eines (embedded) Softwareentwicklers. Die meisten StateMachine Frameworks für C++ sind aber sehr kompliziert und verwenden unglaublich viel Templates oder hängen von irgendwelchen Bibliotheken ab. Hab grad Urlaub und hab mir gedacht ich bau mir jetzt endlich ein eigenes Statemachine-Framework. Überraschenderweise war das recht schnell erledigt (ca. 2 Tage). So viel muss ein StateMachine-Framework ja eigentlich garned können hab ich gemerkt. Was mich sehr gewundert hat ist, dass sowas wie parallele States in vielen Frameworks garnicht möglich sind oder sehr aufwendig. Rausgekommen sind im ersten Schuss etwa 100 Zeilen Code in einer einzigen Headerdatei. Auch wenn ich beim Testen recht leicht auf 100% Coverage kam, bin ich nicht sicher, dass das Ding fehlerfrei ist. Also erstmal vorsichtig damit – Wenn du Fehler findest, bitte her damit. Ich hab bestimmt ein paar versteckt 🙂
Features
Eventbasiert
Einfache Initialisierung (alles an einer Stelle bzw. Methode)
SubStates und parallelle States
Single header mit nur etwa 130 LOC – reinkopieren und loslegen – keine Abhängigkeiten zu irgendwelchen Bibliotheken – STL only
Interface mit nur einer Methode die lediglich Events entgegennimmt
Fast keine Templates – C++11 – auch für alte Compiler
Geeignet für Embedded-Systeme (auch Bare-Metal mit wenig RAM, FreeRTOS, Arduino, mbed)
Verschachtelbar – mehrere StateMachines in einem System einfach möglich
Wenig Speicherbedarf – eine einfache StateMachine braucht ca. 1,4 kB (gemessen auf ESP32 mit Arduino)
Schnell – Stateübergang in 5 us auf PC. Auf ESP32 in 75 us (ohne Logging)
Initialisierung aller States, Transitions und Reactions in nur einer überschriebenen Methode
Einfach zu verbinden mit dem Rest des Systems – kleine Aktionen über Lambdas die mit dem „Rest“ verknüpfen
So einfach, dass durch das kleine Beispiel bereits getestet mit 100% LineCoverage (coverage als html liegt mit im Repo)
Update 2024-11-04: Hab einen Timer-Mechanismus eingebaut (ist zwar kein standard StateMachine-Feature, hab ich aber schon oft gebraucht)
Das Repo liegt hier: https://bitbucket.org/bobbery/mystatemachine hab qmake verwendet, damit mans leicht im QtCreator aufmachen und übersetzen kann. Braucht natürlich kein Qt – aber für ein Makefile für jede Plattform bin ich zu faul und qmake hab ich viel lieber als CMake
Das ist die StateMachine – ist einfach nur ein Header mit knapp über 100 LOC
/* Copyright 2024 stefan.box2code.de
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
#ifndef STATEMACHINE_H
#define STATEMACHINE_H
#include <functional>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <vector>
#define LOG_EVENTS
#define LOG_STATES
template<class StateID, class EventID>
class StateMachine
{
public:
StateMachine() {}
void react(EventID e) { react_p(e); }
void handleTimerTick() { handleTimerTick_p(); }
virtual void init() = 0; // overwrite it to create your machine
virtual ~StateMachine() {}
protected:
void react_p(EventID e)
{
#ifdef LOG_EVENTS
if (m_eventNames.find(e) != m_eventNames.end()) {
std::cout << "on" << m_eventNames.find(e)->second << "()" << std::endl;
}
#endif
bool transitOk = true;
for (StateID sid : m_activeStates) {
if (m_reactions.find(std::make_pair(sid, e)) != m_reactions.end()) {
if (!m_reactions.find(std::make_pair(sid, e))->second()) {
transitOk = false;
}
}
}
if (transitOk) {
transit(e);
}
}
void addInitialState(StateID sid, const std::string &name, StateID parent = StateID::NumStates)
{
m_stateNames[sid] = name;
if (parent != StateID::NumStates) {
m_childStates[parent].insert(sid);
} else {
m_activeStates.insert(sid);
}
m_initials[parent].insert(sid);
}
void addState(StateID sid, const std::string &name, StateID parent = StateID::NumStates)
{
m_stateNames[sid] = name;
if (parent != StateID::NumStates) {
m_childStates[parent].insert(sid);
}
}
void addEventName(EventID eid, const std::string &name) { m_eventNames[eid] = name; }
void addReaction(StateID sid, EventID eid, std::function<bool()> fun)
{
m_reactions.emplace(std::make_pair(sid, eid), fun);
}
void addEntry(StateID sid, std::function<bool()> fun) { m_entry.emplace(sid, fun); }
void addExit(StateID sid, std::function<bool()> fun) { m_exit.emplace(sid, fun); }
void addTransition(StateID sid, EventID eid, StateID target)
{
m_transitions.emplace(std::make_pair(sid, eid), target);
}
void transit(EventID e)
{
std::set<StateID> newActiveStates;
for (StateID id : m_activeStates) {
if (m_transitions.find(std::make_pair(id, e)) != m_transitions.end()) {
StateID activated = m_transitions.find(std::make_pair(id, e))->second;
newActiveStates.insert(activated);
if (m_initials.find(activated) != m_initials.end()) {
for (StateID subInitial : m_initials.find(activated)->second) {
newActiveStates.insert(subInitial);
}
}
} else {
newActiveStates.insert(id);
}
}
std::set<StateID> deactivated;
for (StateID id : m_activeStates) {
if (newActiveStates.count(id) == 0) {
deactivated.insert(id);
}
}
for (StateID id : deactivated) {
if (m_childStates.find(id) != m_childStates.end()) {
for (StateID chId : m_childStates.at(id)) {
newActiveStates.erase(chId);
if (m_exit.find(chId) != m_exit.end()) {
m_exit.at(chId)();
}
}
}
if (m_exit.find(id) != m_exit.end()) {
#ifdef LOG_STATES
std::cout << "onExit() of " << m_stateNames.at(id) << std::endl;
#endif
m_exit.at(id)();
}
}
for (StateID id : newActiveStates) {
if (m_activeStates.count(id) == 0) {
if (m_entry.find(id) != m_entry.end()) {
#ifdef LOG_STATES
std::cout << "onEntry() of " << m_stateNames.at(id) << std::endl;
#endif
m_entry.at(id)();
}
}
}
m_activeStates = newActiveStates;
#ifdef LOG_STATES
std::cout << "Current States: ";
for (StateID id : m_activeStates) {
if (m_stateNames.find(id) != m_stateNames.end()) {
std::cout << m_stateNames.find(id)->second << " ";
}
}
std::cout << std::endl;
#endif
}
void handleTimerTick_p()
{
bool timerExpired = false;
for (auto timer : m_timers) {
if (timer.second < 0) {
continue;
}
m_timers[timer.first] = --timer.second;
if (timer.second == 0) { // timer is expired
m_timers[timer.first] = -1; // timer is stopped
timerExpired = true;
}
}
if (timerExpired) {
react_p(EventID::TimerExpired);
}
}
bool isTimerExpired(size_t timerNum)
{
if (m_timers.find(timerNum) == m_timers.end()) {
return false;
}
return m_timers.at(timerNum) < 0;
}
void startTimer(size_t timerNum, int timeInMs) { m_timers[timerNum] = timeInMs; }
void stopTimer(size_t timerNum) { m_timers[timerNum] = -1; }
std::map<StateID, std::string> m_stateNames;
std::map<EventID, std::string> m_eventNames;
std::map<StateID, std::set<StateID>> m_initials;
std::map<std::pair<StateID, EventID>, StateID> m_transitions;
std::set<StateID> m_activeStates;
std::map<StateID, std::set<StateID>> m_childStates;
typedef std::function<bool()> reactFun;
std::map<std::pair<StateID, EventID>, reactFun> m_reactions;
std::map<StateID, reactFun> m_entry;
std::map<StateID, reactFun> m_exit;
std::map<size_t, int> m_timers;
};
#endif // STATEMACHINE_H
Und ein Besispiel
Das Beispiel ist bewusst einfach und hoffentlich leicht verständlich. Eigentlich wollt ich nur was was mit dem man gut parallelle States demonstrieren kann. Das Diagramm ist mit dem QtCreator gezeichnet:
/* Copyright 2024 stefan.box2code.de
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
#include "statemachine.h"
#define MEASURE_TIME
#ifdef MEASURE_TIME
#include <chrono>
#endif
enum class MyStateID {
Startup,
Running,
Lampe1An,
Lampe1Aus,
Lampe2An,
Lampe2Aus,
NumStates
}; // you should always add NumStates on end
enum class MyEventID {
TimerExpired, // must always be there
On,
Off,
Toggle,
NumEvents
}; // you should always add NumEvents on end
class MyApi // The Interface to the Rest of your Application
{
public:
virtual bool checkBatteryFull()
{
std::cout << "Implement battery check" << std::endl;
return false;
}
virtual void turnOnLed() { std::cout << "Implement turn on LED" << std::endl; }
virtual void playAudio() { std::cout << "Implement playing audio" << std::endl; }
virtual void sendEventToAnotherSm(MyEventID)
{
std::cout << "Send event to another state machine in your system" << std::endl;
// anotherStateMachine.react(e);
}
virtual ~MyApi() {}
};
class MyStateMachine : public virtual StateMachine<MyStateID, MyEventID>
{
public:
MyStateMachine(MyApi &api)
: m_api(api)
{
init();
}
void init() override
{
addInitialState(MyStateID::Startup, "Startup");
addReaction(MyStateID::Startup, MyEventID::Off, [&]() {
std::cout << "Hey a Off-Event in Startup" << std::endl;
if (m_api.checkBatteryFull()) { // this is the easy way to call the rest of your system
return true; // must return true to do corresponding transition
}
return false;
});
addTransition(MyStateID::Startup,
MyEventID::Off,
MyStateID::Running); // this one will be forbidden by reaction above
addTransition(MyStateID::Startup, MyEventID::On, MyStateID::Running);
addState(MyStateID::Running, "Running");
addEntry(MyStateID::Running, [&]() {
std::cout << "Entry() of Running" << std::endl;
startTimer(42, 500); // start timer 42 with 500 ms
m_api.turnOnLed(); // this is the easy way to call the rest of your system
return true; // return what ever you want - doesnt matter
});
addExit(MyStateID::Running, [&]() {
std::cout << "Exit() of Running" << std::endl;
stopTimer(42); // timer 42 is now stopped
return false; // return what ever you want - doesnt matter
});
addTransition(MyStateID::Running, MyEventID::Off, MyStateID::Startup);
addTransition(MyStateID::Running, MyEventID::TimerExpired, MyStateID::Startup);
addReaction(MyStateID::Running, MyEventID::TimerExpired, [&]() {
if (isTimerExpired(42)) {
std::cout << "Timer 42 has expired" << std::endl;
return true;
}
return false;
});
addInitialState(MyStateID::Lampe1An, "Lampe1An", MyStateID::Running);
addTransition(MyStateID::Lampe1An, MyEventID::Toggle, MyStateID::Lampe1Aus);
addState(MyStateID::Lampe1Aus, "Lampe1Aus", MyStateID::Running);
addTransition(MyStateID::Lampe1Aus, MyEventID::Toggle, MyStateID::Lampe1An);
addEntry(MyStateID::Lampe1Aus, []() {
std::cout << "Entry() of Lampe1Aus" << std::endl;
return true; // return what ever you want - doesnt matter
});
addExit(MyStateID::Lampe1Aus, []() {
std::cout << "Exit() of Lampe1Aus" << std::endl;
return false; // return what ever you want - doesnt matter
});
addState(MyStateID::Lampe2An, "Lampe2An", MyStateID::Running);
addTransition(MyStateID::Lampe2An, MyEventID::Toggle, MyStateID::Lampe2Aus);
addTransition(MyStateID::Lampe2An, MyEventID::Off, MyStateID::Startup);
addReaction(MyStateID::Lampe2An, MyEventID::Off, []() {
std::cout << "Hey a Off-Event in Lampe2An" << std::endl;
return false; // returns false so the transition is skipped
});
addInitialState(MyStateID::Lampe2Aus, "Lampe2Aus", MyStateID::Running);
addTransition(MyStateID::Lampe2Aus, MyEventID::Toggle, MyStateID::Lampe2An);
addEventName(MyEventID::On, "On");
addEventName(MyEventID::Off, "Off");
addEventName(MyEventID::Toggle, "Toggle");
}
private:
MyApi &m_api;
};
int main()
{
MyApi myApi;
MyStateMachine m(myApi);
#ifdef MEASURE_TIME
auto start = std::chrono::steady_clock::now();
#endif
m.react(MyEventID::Off);
m.react(MyEventID::On);
m.react(MyEventID::Toggle);
m.react(MyEventID::Toggle);
m.react(MyEventID::Toggle);
m.react(MyEventID::Toggle);
m.react(MyEventID::Off);
m.react(MyEventID::Off);
m.react(MyEventID::On);
m.react(MyEventID::Toggle);
m.react(MyEventID::Toggle);
m.react(MyEventID::Toggle);
m.react(MyEventID::Toggle);
m.react(MyEventID::Off);
m.react(MyEventID::On);
m.react(MyEventID::Off);
m.react(MyEventID::On);
#ifdef MEASURE_TIME
std::cout << "Elapsed(us)="
<< std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - start)
.count()
<< std::endl;
#endif
for (int ms = 0; ms < 1000; ms++) {
// of course you should call this in your SysTick-Handler
m.handleTimerTick();
}
}
Du siehst schon – Templates brauchst du nur in einer Zeile um deine eigenen Listen für States und Events einbinden zu können.
Die Anbindung an dein restliches System hab ich nur skizziert.
Ich hoffe der Code ist ansonsten selbsterklärend – Viel Spaß damit
Ach ja, noch die Ausgabe des Programms:
onOff()
Hey a Off-Event in Startup
Implement battery check
onOn()
onEntry() of Running
Entry() of Running
Implement turn on LED
Current States: Running Lampe1An Lampe2Aus
onToggle()
onEntry() of Lampe1Aus
Entry() of Lampe1Aus
Current States: Running Lampe1Aus Lampe2An
onToggle()
onExit() of Lampe1Aus
Exit() of Lampe1Aus
Current States: Running Lampe1An Lampe2Aus
onToggle()
onEntry() of Lampe1Aus
Entry() of Lampe1Aus
Current States: Running Lampe1Aus Lampe2An
onToggle()
onExit() of Lampe1Aus
Exit() of Lampe1Aus
Current States: Running Lampe1An Lampe2Aus
onOff()
Exit() of Lampe1Aus
onExit() of Running
Exit() of Running
Current States: Startup
onOff()
Hey a Off-Event in Startup
Implement battery check
onOn()
onEntry() of Running
Entry() of Running
Implement turn on LED
Current States: Running Lampe1An Lampe2Aus
onToggle()
onEntry() of Lampe1Aus
Entry() of Lampe1Aus
Current States: Running Lampe1Aus Lampe2An
onToggle()
onExit() of Lampe1Aus
Exit() of Lampe1Aus
Current States: Running Lampe1An Lampe2Aus
onToggle()
onEntry() of Lampe1Aus
Entry() of Lampe1Aus
Current States: Running Lampe1Aus Lampe2An
onToggle()
onExit() of Lampe1Aus
Exit() of Lampe1Aus
Current States: Running Lampe1An Lampe2Aus
onOff()
Exit() of Lampe1Aus
onExit() of Running
Exit() of Running
Current States: Startup
onOn()
onEntry() of Running
Entry() of Running
Implement turn on LED
Current States: Running Lampe1An Lampe2Aus
onOff()
Exit() of Lampe1Aus
onExit() of Running
Exit() of Running
Current States: Startup
onOn()
onEntry() of Running
Entry() of Running
Implement turn on LED
Current States: Running Lampe1An Lampe2Aus
Elapsed(us)=123
Timer 42 has expired
Exit() of Lampe1Aus
onExit() of Running
Exit() of Running
Current States: Startup
by Stefan·Kommentare deaktiviert für Kundenprojekt CFB ( CORmunity – Fotobox )
Codename
CFB ( CORmunity – Fotobox )
Kurzname
corCAM
Kunde:
Unsere CORmunity bei Corscience
Projektzeitraum:
etwa 1,5 Monate
Produktive Stunden:
etwa 60
Tarif:
CORmunity-4-CORmunity ( nur Material )
Einsatzzweck:
Partyfotos 🙂
Quellcode:
Applikation etwa 1000 LOC
Neu erworbene Skills:
Für Raspi immer das offizielle Netzteil verwenden ( sonst undervoltage ) Bügelbrett eignet sich hervorragend als Montageplattform 🙂 Fotografieren und Beleuchtung ( natürlich immer noch totaler Laie dadrin 🙂 )
Rückblick:
Absoluter Funfaktor – so ein geiles Projekt 🙂 Danke an Sina für die super Idee und den Projektanstoß Danke an Viktoria für die hervorragende Projektleitung und deine rat- und tatkräftige Unterstützung Danke an Jörg für die Finanzierung und megaschnelle Bestellung unserer benötigten Komponenten Danke an Sebastian für die tollen Tipps zum Fotografieren
by Stefan·Kommentare deaktiviert für Kundenprojekt RDP (Radarpistole)
Codename
RDP ( Radarpistole )
Kunde:
Hans Reif
Projektzeitraum:
über Weihnachten
Produktive Stunden:
etwa 20
Tarif:
Friends (beer and food 🙂 )
Einsatzzweck:
Geschwindigkeitsmessung mit günstigem Doppler-Radarsensor
Quellcode:
ESP32-FW ca. 100 LOC
Neu erworbene Skills:
Funktionsweise Doppler-Radar Schaltungssimulation mit EasyEDA und Qucs-S
Rückblick:
Wir sind noch nicht fertig und bauen jetzt das Gerät weiter zu einem autonomen System um, das an der Straße positioniert, Fahrzeuge auf beiden Fahrspuren erfasst und protokolliert
by Stefan·Kommentare deaktiviert für Kundenprojekt PMGWeb (Webinterface für ein Pegelmessgerät)
Codename
PMGWeb (Webinterface für Pegelmessgerät)
Kunde:
Hans Reif
Projektzeitraum:
etwa 2 Monate
Produktive Stunden:
etwa 20
Tarif:
Friends ( beer and food 🙂 )
Einsatzzweck:
Pegelstände von Flüssigkeiten darstellen und Messung triggern
Quellcode:
Device ESP 300 LOC WEB-Page 300 LOC
Neu erworbene Skills:
Kann jetzt besser und schneller Webanwendungen mit UI5 bauen ApexCharts funktionieren recht gut zusammen mit UI5
Rückblick:
Es war ein sehr schönes Projekt, Hans war in meiner Lehrzeit mein Elektronikervorbild und es versteht sich von selbst dass wir viel zu quatschen und viel Spaß hatten 🙂
Kundenstimme:
Es hat richtig Spaß gemacht, dieses Projekt mit Stefan zu machen. Es funktioniert super, genauso wie ich es mir vorgestellt habe. Stefan hat eine sehr professionelle Arbeit abgeliefert. Vielen Dank. Freue mich schon auf das nächste Projekt.
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 🙂
by Stefan·Kommentare deaktiviert für Kundenprojekt CLM ( Curved-Light-Modulator )
Codename
CLM (Curved Light Modulator)
Kunde:
Jörg vom Deep Zone Experience Project
Projektzeitraum:
etwa 5 Monate
Produktive Stunden:
etwa 60
Tarif:
CORmunity ( pay in beer 🙂 )
Einsatzzweck:
verraten wir nicht 🙂
Quellcode:
Device 200 LOC PC-Design-SW 550 LOC WEB-Page 260 LOC Android-App 200 LOC
Neu erworbene Skills:
Einfacher und stromsparender Aufbau von batteriebetriebenen Geräten
Eigene Leiterplatten sind schnell entworfen und machen auch für Prototypen Sinn da unglaublich günstig – außerdem macht’s Spaß
Miniaturisierung und Optimierung für den Low-Cost Markt (Materialkosten < 7€ bei Einzelstückzahlen)
Lichtleiter mit 3D-Drucker ( danke Norbert 🙂 )
Rückblick:
Es war ein sehr schönes Projekt, Jörg ist immer gut gelaunt, immer erreichbar, hat sehr gute Ideen, einen Sinn fürs Wesentliche und einen sehr netten Humor
Kundenstimme:
Stefan hat die Projektidee schnell und einfach umgesetzt sowie im Projekt eine Menge guter Ideen eingebracht. Das Ergebnis lässt sich sehen und wir sind auch mit überschaubaren Ausgaben zum Ziel gekommen. Ich freue mich auf die nächste Ausbaustufe.
Mal wieder ein größeres Projekt mit meinem Vermieter und Kumpel Hans zusammen. Ein Android-Fahrradcomputer mit offline Karte, BLE-Sensoren, Aufzeichnung und GPX-Export. Wieder mit meinem neuen Lieblingswerkzeug Dart & Flutter
by Stefan·Kommentare deaktiviert für Cutefish für Ubuntu 20.04
Timo hat neulich den, total schicken, neuen Desktop Cutefish auf YouTube entdeckt.
Wir sind von der Schnelligkeit, Optik und Bedienung total begeistert.
Außerdem ist Cutefish in QT programmiert und benutzt einige Teile von KDE
(QT war immer mein Argument warum ich KDE und nichts anderes verwende 🙂 ).
Leider gibt es nur Downloads für ArchLinux und Manjaro.
Drum hier die Pakete für die letzte LTS von Ubuntu:
Schon die dritte Version des einfachen Getränkeautomats, die ich baue – Selbes Prinzip aber diesmal völlig neue Bauweise. Diesmal hab ich als Basis einfach ein sehr günstiges Android-Tablet verwendet. Für die Implementierung natürlich mein neues Lieblingswerkzeug Dart&Flutter.
Eine „ungebrandede“ Version des Mat-O-Id gibt es bald bei F-Droid
Mal wieder ein richtiges Unsinnsprojekt 🙂 Ein Nudelkocher mit App- bzw. PC-Steuerung über Bluetooth Noch nicht ganz fertig – aber mal die ersten Bilder
Ein Gerät zum Bezahlen von Getränken, mit einem RFID-Token, auf meiner Arbeit. Mittlerweile die zweite Version mit schönerer GUI durch TouchGFX. Zum alten Mat-O-Mat, der schon seit etwa zwei Jahren im Einsatz ist, schreibe ich was wenn dieser erfolgreich durch die neue Version abgelöst ist.
by Stefan·Kommentare deaktiviert für Warm-Wasser Steuerung
Projekt mit meinem Vermieter Hans zusammen – Eine Warm-Wasser Steuerung für ein Ferienhaus. Das Projekt verwendet mein neues Lieblingsboard, das man zur Zeit für einen sagenhaften Preis bei Amazon bekommt. Hab mittlerweile schon 7 davon gekauft, eines kaputt gemacht und 4 davon verbaut. Die ersten Gehversuche damit waren der Mat-O-Mat und zwei Analysegeräte für eine Pumpe. Zum Mat-O-Mat kommt bald was. Verwendet hab ich dazu mbed und die ETL – eine spitzen Kombination, finde ich.
by Stefan·Kommentare deaktiviert für Einfaches EKG mit ARM
Vor kurzem bin ich von AVR Mikrocontrollern auf ARM umgestiegen. Nach ein wenig Herumprobieren fehlt jetzt ein anständiges Projekt. Ich hab lange überlegt und jetzt endlich was Passendes gefunden. Dann mal los …
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…
by Stefan·Kommentare deaktiviert für Koordinatenrechner
Mal eine kleine Fingerübung für zwischendurch.
Auf Arbeit müssen wir oft zwischen verschiedenen Koordinatensystemen konvertieren.
Abhilfe soll der kleine Rechner hier schaffen, der hoffentlich noch um viele Funktionen erweitert wird.
by Stefan·Kommentare deaktiviert für NDS Plugin für QGIS
Hab ich mir schon lange gewünscht – ein einfaches Plugin zum Anzeigen von NDS Kacheln für QGIS.
War super einfach. Kein Wunder, dass es schon so viele QGIS Plugins gibt.
by Stefan·Kommentare deaktiviert für Bestes Buch zu Python
Mein Lieblingsbuch zu Python – schon etwas älter aber immer noch aktuell. Geht auch auf wichtige Themen wie Grafik, Datenbanken und XML ein. Sehr schön geschrieben
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
by Stefan·Kommentare deaktiviert für ZECHA Track & Time
Mal wieder ein größeres Projekt in QT – noch nicht ganz fertig aber mal ein Zwischenstand.
Ausschlaggebend war ein Gespräch mit Basti vor etwa drei Wochen.
by Stefan·Kommentare deaktiviert für Basiskurs Mikrocontroller
Wieder ein Buch von Burkhard Kainka. Damals 2000 im zweiten Jahr meiner Ausbildung zum Elektroniker erschien bei Elektor eine
Artikelserie zu Mikrocontrollern auf Basis des 8051. Grund genug für mich damals die Zeitschrift zu abonnieren.
Das Buch ist eine Zusammenfassung der damaligen Artikelserie.
by Stefan·Kommentare deaktiviert für PC Schnittstellen
Ein Buch aus den frühesten Anfängen meiner Programmierzeit von meinem lieblings Elektor Author Burkhard Kainka.
In dem Buch ist erklärt wie mit dem PC (damals noch unter DOS) externe Peripherie anzusprechen ist.
Voll mit tollen Beispielen und Programmlistings in Pascal und Basic
Der *Guru* war mir ein treuer Begleiter beim verstehen von Linux und ich konnte viele meiner Mitschüler und sogar meinen Lehrer für Betriebssysteme, in der Technikerschule, für dieses Buch begeistern.
Clean Code von Robert C. Martin ist eines meiner absoluten Lieblingsbücher.
Ich hab es inzwischen bestimmt zwei drei mal gelesen und dabei immer wieder was Neues für mich entdeckt.
Der Grundgedanke ist einfach – Quellcode wird öfter gelesen als geschrieben und man sollte sich Mühe geben
diesen leicht lesbar zu gestalten. Das Buch ist voll von guten Beispielen und sinnvollen Regeln die wirklich helfen.
by Stefan·Kommentare deaktiviert für Band mit Basti, Renz und Ferdi
Hm mal schaun ob ich noch was finde. Eigentlich schade – mit den Leuten mit denen ich noch am meisten Kontakt hab und ich hab keine Fotos oder Aufnahmen. Hat immer riesen Spaß gemacht Freitag Abend beim Renz im Keller …
by Stefan·Kommentare deaktiviert für Browser für Requirements
Nach Langem endlich mal wieder ein Projekt in Qt das richtig Spaß macht 🙂 Spaß macht es vor allem durch die vielen Ideen, Verbesserungswünsche und Bugreports von Kollegen, die das Tool verwenden. An dieser Stelle noch mal vielen Dank dafür. Ich hoffe es gibt noch viel zu tun …
by Stefan·Kommentare deaktiviert für Erstes Projekt mit Wt
Einige wissen vielleicht wie das ist – man findet eine coole Bibliothek und will damit unbedingt was tolles machen. Glücklicherweise hatte ein Kollege ein passendes Problem, das sich gut für ein erstes Projekt eignete …
by Stefan·Kommentare deaktiviert für Kleine Anekdotensammlung
Wer mit mir schon gearbeitet hat weiß, dass ich ein Fan von Anekdoten bin. Oft sagt nämlich ein kurzer Satz mehr aus als das was in einem ganzen Buch niedergeschrieben ist …
by Stefan·Kommentare deaktiviert für Zauberkünstler
Zauberkünstler ist ein kleines Programm, dass ich vor einigen Jahren mit C++ und QT, für einen mir bekannten Zauberer geschrieben habe. Es war damals (ca. 2005) eines meiner ersten größeren Programme, die ich mit C++ geschrieben habe. Daher hat es vielleicht nicht gerade das beste objektorientierte Design, ist aber sehr einfach aufgebaut und gut zu verstehen.