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.
Continue readingKategorie: Mikrocontroller
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
Du brauchst Micropython und die Datei https://github.com/micropython/micropython/blob/master/examples/bluetooth/ble_advertising.py aus den Micropython-Beispielen auf deinem Device. Natürlich hab ich mich auch großzügig an den Beispielen dort bedient – ist doch klar 🙂
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()
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
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 |
Kundenstimme: | Später, wenn wir ganz fertig sind 🙂 |
ESP32-Grafik mit LVGL und Micropython
Geile Grafik, mit wenig Aufwand, für den ESP32
Nobb-E
Abschiedsgeschenk für Norbert
Message in a Bottle
Leo 60 Wildcam
Eine Wildkamera im Nistkasten für meinen Paten Leo zum 60ten
corBoxide
Kleiner günstig aufzubauender Luftwächter für unsere Besprechungsräume auf Arbeit
WheelOfFoodtruck
Eine kleine, superlustige Bastelei für unseren Parkplatz auf Arbeit.
Embedded Linux remote debugging mit RPI zero, Buildroot und QT
LoRaMotion
Eine kleine Anwendung für LoRa ohne WAN – Ein Funk-Bewegungsmelder für meinen Paten Erich
LoRa ohne Gateway
Kurzer Reichweitenversuch mit zwei günstigen LoRa-Modulen ohne Verwendung eines Gateways.
Chicken-Cam
Mal wieder ein kleines Unsinnsprojekt über Ostern. Eine Webcam für Hühner zum Geburtstag meiner Schwester.
Mr. Noodle
Mal wieder ein richtiges Unsinnsprojekt 🙂
Ein Nudelkocher mit App- bzw. PC-Steuerung über Bluetooth
Noch nicht ganz fertig – aber mal die ersten Bilder
Mat-O-Mat
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.
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.
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 …
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…
Dattelkiste
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
MyBoot
MyBoot ist ein kleiner Bootloader, den ich vor einigen Jahren für ATMega-AVRs geschrieben habe.
WavPlayer
WavPlayer ist eine kleine Schaltung, die Wav-Dateien von einer SD-Karte abspielen kann