/* -------------------------------------------------------- event_ctrl.ino Eventbasierendes System, hier ganz bewusst ohne Interrupts 03.05.2026 R. Seelig -------------------------------------------------------- */ #include "sn74hc595.h" /* ---------------------------------------------------------------------- Objektinstanz von shiftreg595 erstellen Usage: shiftreg595::shiftreg595(uint8_t clk, uint8_t dat, uint8_t stb) ---------------------------------------------------------------------- */ shiftreg595 sr(7, 5, 6); /* ---------------------------------------------------------------------- Dip-Schalter an Pin 4 und Pin 3 angeschlossen ---------------------------------------------------------------------- */ constexpr int dipkey_0= 4; constexpr int dipkey_1= 3; /* ---------------------------------------------------------------------- Trimmer an A0 angeschlossen ---------------------------------------------------------------------- */ constexpr int TimerA_adcpin= A0; /* ---------------------------------------------------------------------- LED angeschlossen an Pin 13 (onboard LED) ---------------------------------------------------------------------- */ constexpr int blinkLed = 13; /* ---------------------------------------------------------------------- Eventcodes ---------------------------------------------------------------------- */ constexpr uint8_t TimerA_event = 0x80; // Event TimerA mit ADC fuer abgelaufene Zeit constexpr uint8_t TimerB_event = 0x40; // Event TimerB abgelaufene Zeit constexpr uint8_t Dipkeychange_event = 0x20; // Event DIP-Schalterzustand hat sich geaendert constexpr uint8_t UartChar_event = 0x10; // Event Zeichen auf UART eingetroffen /* ---------------------------------------------------------------------- globales fuer Eventsystem ---------------------------------------------------------------------- */ uint8_t event = 0; // nimmt eingegangene Events nach event_scan auf uint8_t dipkey_state; /* ---------------------------------------------------------------------- globales fuer Lauflicht ---------------------------------------------------------------------- */ uint8_t *ll_muster; uint8_t ll_len; uint8_t ll_index; /* ---------------------------------------------------------------------- globaler String, der die eingehenden Zeichen auf der UART entgegen nimmt. ---------------------------------------------------------------------- */ constexpr uint8_t uart_maxeventbuf = 80; char uart_eventbuf[uart_maxeventbuf] = {"\0"}; uint8_t strreadln_ready = 0; /* ---------------------------------------------------------------------- Lauflichtmuster / -programme. Element 0 ist Laengenbyte und beinhaltet die Anzahl der abzuspielenden Leuchtmuster ---------------------------------------------------------------------- */ uint8_t lauf_bmp0[] = {14, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02 }; uint8_t lauf_bmp1[] = {16, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00 }; uint8_t lauf_bmp2[] = { 4, 0x33, 0x66, 0xcc, 0x99 }; uint8_t lauf_bmp3[] = { 8, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x81 }; /* ---------------------------------------------------------------------- TIMERADC Klasse ---------------------------------------------------------------------- */ class TIMERADC { private: uint32_t starttime; uint8_t adcpin; uint16_t minvalue; uint8_t event; public: /* ---------------------------------------------------------------------- TIMERADC::TIMERADC ---------------------------------------------------------------------- */ TIMERADC(uint8_t pin, uint16_t minval, uint8_t tevent) { adcpin = pin; minvalue = minval; event = tevent; } /* ---------------------------------------------------------------------- TIMERADC::setstarttime erfasst, wann TimerA gestartet wurde ---------------------------------------------------------------------- */ void TIMERADC::setstarttime(void) { starttime = millis(); } /* ---------------------------------------------------------------------- TIMERADC::getadcmillis liest den ADC, an dem der Potentiometer angeschlossen ist und berechnet daraus die Mindestlaufzeit fuer TimerA. 10-Bit ADC entspricht 0..1023 ADC / 3 = 341; 341 + minvalue (40) = 381 Rueckgabewerte je nach Potenziometereinstellung 40..381 ---------------------------------------------------------------------- */ int TIMERADC::getadcmillis(void) { return ((analogRead(adcpin) / 3) + minvalue); } /* ---------------------------------------------------------------------- TIMERADC::getstate stellt fest, ob die Mindestlaufzeit, die über den ADC eingestellt ist, abgelaufen ist. Rueckgabe: 0x00: wenn nicht abgelaufen TimerA_event: wenn Zeit abgelaufen ---------------------------------------------------------------------- */ uint8_t TIMERADC::getstate(void) { if (millis() > (starttime + getadcmillis())) return event; else return 0x00; } }; /* ---------------------------------------------------------------------- TIMERSOFT Klasse ---------------------------------------------------------------------- */ class TIMERSOFT { private: uint32_t starttime; uint16_t overflow; uint8_t event; public: /* ---------------------------------------------------------------------- TIMERSOFT::TIMERSOFT ---------------------------------------------------------------------- */ TIMERSOFT(uint16_t ovf, uint8_t tevent) { overflow = ovf; event = tevent; } /* ---------------------------------------------------------------------- TIMERSOFT::setstarttime erfasst, wann Timer gestartet wurde ---------------------------------------------------------------------- */ void TIMERSOFT::setstarttime(void) { starttime = millis(); } /* ---------------------------------------------------------------------- TIMERSOFT::getstate stellt fest, ob die Mindestlaufzeit (overflow) fuer TIMERSOFT abgelaufen ist Rueckgabe: 0x00: wenn nicht abgelaufen event: wenn Zeit abgelaufen ---------------------------------------------------------------------- */ uint8_t TIMERSOFT::getstate(void) { if (millis() > (starttime + overflow)) return event; else return 0x00; } /* ---------------------------------------------------------------------- TIMERSOFT::setoverflow neue Overflow-Zeiten in Millisekunden setzen ---------------------------------------------------------------------- */ void TIMERSOFT::setoverflow(uint16_t ovf) { overflow = ovf; } }; /* ---------------------------------------------------------------------- DIPKEY Klasse ---------------------------------------------------------------------- */ class DIPKEY { private: uint8_t pin0; uint8_t pin1; uint8_t state; uint8_t event; public: /* ---------------------------------------------------------------------- DIPKEY::DIPKEY ---------------------------------------------------------------------- */ DIPKEY(uint8_t p0, uint8_t p1, uint8_t tevent) { pin0 = p0; pin1 = p1; state = 0; event= tevent; } /* ---------------------------------------------------------------------- DIPKEY::read liest Schalterstellung Rueckgabe: 0x00 .. 0x03 entsprechend der eingestellten Schalterstellung ---------------------------------------------------------------------- */ uint8_t DIPKEY::read(void) { uint8_t keyval = 0x00; if (digitalRead(pin0)) keyval = 0x01; if (digitalRead(pin1)) keyval |= 0x02; return keyval; } /* ---------------------------------------------------------------------- DIPKEY::getchange stellt fest, ob DIP-Schalterstellung veraendert wurde. Wenn ja, wird in dipkey_state eingetragen und Event fuer Dipkey_getchange ausgeloest. Rueckgabe: 0x00: wenn Schalterstellung unveraendert event: wenn Schalterstellung verändert ---------------------------------------------------------------------- */ uint8_t DIPKEY::getChange(void) { uint8_t keys = read(); if (keys != state) { state = keys; return event; } return 0; } /* ---------------------------------------------------------------------- DIPKEY::getstate liefert aktuell gespeicherten DIP-Schalterzustand ---------------------------------------------------------------------- */ uint8_t DIPKEY::getState(void) { return state; } /* ---------------------------------------------------------------------- DIPKEY::begin initialisiert Pins des 2 pol. Dipschalter als Eingang ---------------------------------------------------------------------- */ void DIPKEY::begin() { pinMode(pin0, INPUT_PULLUP); pinMode(pin1, INPUT_PULLUP); state= read(); } }; /* ---------------------------------------------------------------------- 2 Timerobjekte aus Klassen bilden ---------------------------------------------------------------------- */ TIMERADC TimerA(TimerA_adcpin, 40, TimerA_event); TIMERSOFT TimerB(500, TimerB_event); /* ---------------------------------------------------------------------- dipkey - Objekt bilden ---------------------------------------------------------------------- */ DIPKEY dipkey(dipkey_0, dipkey_1, Dipkeychange_event); /* ---------------------------------------------------------------------- str_addbufch fuegt dem Eingangsbuffer auf der UART ein Zeichen hinten an. Im Falle eines Backspace / Del loescht es das letzte Zeichen aus dem Buffer ---------------------------------------------------------------------- */ void str_addbufch(char *s, char ch) { uint8_t len = strlen(s); // Cr und nL NICHT in den String aufnehmen if (ch== 0x0d || ch== 0x0a) return; // DEL - Backspacezeichen => letztes Zeichen loeschen if (ch == 0x08 || ch == 0x7F) { if (len > 0) s[len - 1] = '\0'; return; } // alle anderen Zeichen hinten anhaengen, so lange der Eingabe- // nicht voll ist. if (len < (uart_maxeventbuf - 1)) { s[len] = ch; s[len + 1] = '\0'; } } /* ---------------------------------------------------------------------- io_init initialisiert I/O Anschluesse fuer Benutzung Eingang / Ausgang: Dip-Schalter, Trimmer, blinkLed ---------------------------------------------------------------------- */ void io_init(void) { pinMode(dipkey_0, INPUT_PULLUP); pinMode(dipkey_1, INPUT_PULLUP); pinMode(TimerA_adcpin, INPUT); pinMode(blinkLed, OUTPUT); } /* ---------------------------------------------------------------------- ll_pointerget (LaufLichtMuster-PointerGet) liefert einen Zeiger auf das entsprechende Lauflichtmuster ---------------------------------------------------------------------- */ uint8_t *ll_pointerget(uint8_t mode) { switch (mode) { case 0: return lauf_bmp0; case 1: return lauf_bmp1; case 2: return lauf_bmp2; case 3: return lauf_bmp3; default: return lauf_bmp0; } } /* ---------------------------------------------------------------------- ll_setmusterseq setzt ein Lauflichtmuster entsprechend den angegebenen Arrays. Ein Pointer auf ein Muster wird gesetzt, die Laenge des Musters erfasst und ein Index auf das 1. Zeichen des Musters gesetzt und dieses auch ausgegeben. ---------------------------------------------------------------------- */ void ll_setmusterseq(uint8_t ll_seq) { ll_muster = ll_pointerget(ll_seq); ll_len = *ll_muster; ll_index = 1; sr.setvalue(*(ll_muster + ll_index), 1); } /* ---------------------------------------------------------------------- ll_step das naechstge Lauflichtbitmuster anzeigen ---------------------------------------------------------------------- */ void ll_step(void) { if (ll_index < ll_len) ll_index++; else ll_index = 1; sr.setvalue(*(ll_muster + ll_index), 1); } /* --------------------------------------------------------- ------------- blinkLed_toggle togglet LED an Pin ---------------------------------------------------------------------- */ void blinkLed_toggle(void) { static uint8_t led = 0x01; led ^= 0xff; digitalWrite(blinkLed, led & 0x01); } /* --------------------------------------------------------- ------------- event_scan ersetzt hier im Prinzip Interrupts, in dem jegliche Quelle, die ein Event "erzeugen" kann, abgefragt wird. ---------------------------------------------------------------------- */ uint8_t event_scan(void) { uint8_t event; event = TimerA.getstate(); if (event) return event; event = TimerB.getstate(); if (event) return event; event = dipkey.getChange(); if (event) return event; if (Serial.available() > 0) return UartChar_event; return 0; } /* --------------------------------------------------------- ------------- event_handler stellt eingegangene "Events" fest und startet den Events zuge- ordneten Funktionen. ---------------------------------------------------------------------- */ void event_handler(void) { uint8_t event; char ch; do { event = event_scan(); switch (event) { // mit Analogwert einstellbarer TimerA case TimerA_event: ll_step(); event= 0; TimerA.setstarttime(); break; // TimerB case TimerB_event: blinkLed_toggle(); event= 0; TimerB.setstarttime(); break; // Dip-Schalteraenderung case Dipkeychange_event: event= 0; ll_setmusterseq(dipkey.getState()); break; // eingegangenes Zeichen auf Serial (uart) case UartChar_event: // Zeichen dem Buffer nur hinzufuegen, wenn noch kein // Returnzeichen gesendet wurde if (!strreadln_ready) { ch= Serial.read(); // Zeichen lesen Serial.print(ch); // und als Echo ausgeben // bei Backspace / Delete und min. 1 Zeichen Stringlaenge // Zeichen links vom Cursor loeschen if (strlen(uart_eventbuf)> 0) { if ((ch== 0x08) || (ch== 0x7f)) Serial.print("\b \b"); } str_addbufch(&uart_eventbuf[0], ch); // Zeichen im Buffer ablegen if ((ch== 0x0d) || (ch== 0x0a)) { Serial.println(""); strreadln_ready= 1; } } event= 0; break; default : break; } } while (event); } /* ---------------------------------------------------------------------- setup ---------------------------------------------------------------------- */ void setup() { Serial.begin(115200); Serial.println(""); Serial.println("---------------------------------"); Serial.println(" Event-Handler mit Arduino: Demo "); Serial.println("---------------------------------"); Serial.print("Eingabe aktiv\n\r\n\r> "); sr.begin(); // Schieberegister "starten" dipkey.begin(); io_init(); // ADC-Pin als Analogeingang, blinkLED als Ausgang ll_setmusterseq(dipkey.getState()); // Lauflichtmuster entspr. der Schalterstellung setzen TimerA.setstarttime(); // die erste Ablaufzeit TimerA setzen TimerB.setoverflow(250); // Intervallzeit TimerB TimerB.setstarttime(); // die erste Ablaufzeit TimerB setzen blinkLed_toggle(); // erste Ausgabe fuer die blinkende LED } /* ---------------------------------------------------------------------- loop ---------------------------------------------------------------------- */ void loop() { event_handler(); // hier alle dem Event-Handler nachgeordneten Funktionen einfuegen // es ist ein kompletter String eingegangen if (strreadln_ready) { Serial.print("Eingegangener String war: "); Serial.println(uart_eventbuf); Serial.print("\n\r> "); uart_eventbuf[0]= '\0'; // Eingabebuffer loeschen strreadln_ready= 0; // und lesen wieder freigeben } }