• Die Forumsregeln und Nutzungsbedingungen findet ihr in der Navbar unter Impressum !
    Bitte unbedingt beachten!
    Wie überall im Leben gibt es Spielregeln, so auch hier!
    Die Datenschutzerklärung zum DSGVO findet ihr ebenfalls in der Navbar unter Datenschutzerklärung !
    Hinweis nach DSGVO :
    Es ist hier keinerlei Angabe erforderlich. Alle Angaben in diesem Bereich sind öffentlich sichtbar und werden freiwillig gemacht. Mit einem Eintrag in diesem Bereich erkenne ich dieses an, und bestätige die Datenschutzerklärung zur DSGVO für das Forum gelesen zu haben.

    Danke
  • Hallo Gast, beschränke dich hier bitte auf den Bereich der Elektronik. Die Fahrzeuge oder Gebäude, wo diese Elektronik eingebaut wird bitte in dem passenden Fachbereiich behandeln. Auch wenn Teile des Projektes dadurch im Forum doppelt vorhanden sind! Danke.

Arduino Ein Multiswitch à la EKMFA des Beier Moduls

gismow

Active member
Registriert
03.03.2012
Beiträge
2.203
Moin, moin...

In meinen Fahrplatinen benötige ich eine Möglichkeit Funktionen auszulösen. Gleichzeitig wollte ich aber keinen weiteren Kanal "verschwenden" und habe mich entschieden mich einfach auf den EKMFA Kanal meiner Beier USM-RC-2 aufzuschalten. Also habe ich einen Multiswitch entwickelt der sich exakt so verhält wie der EKMFA von Beier bedient wird. Das Modul zählt die Tipps nach oben bzw. unten und führt dann eine definierte Funktion aus.

So kann z. B. das Beier die Tipps bis 3 für oben (A) und unten (D) verwenden und mein Multiswitch kann dann z. B. ab 4 mal getippt eigene Funktionen ausführen, die das Beier Modul dann ignoriert. Natürlich kann auch passend zu einer Beier Funktion eine Funktion im Multiswitch ausgelöst werden.

Der Code ist ausgelegt für den Arduino Uno und lauscht an Pin2 auf ein Servosignal.

Beim Start geht das Modul in einen "Startup" Modus. Hier werden erst einmal eine definierte Anzahl valider Signale gezählt um sicher zu stellen dass das Modul erst "los legt" wenn auch Servo Signale vorhanden sind. Sind diese empfangen wird der dann aktuelle Servowert des Kanals als Mittelwert des Kanals verwendet. Aus diesem Wert werden dann Schwellenwerte berechnet ab denen ein Tipp gezählt wird. Wenn der Startup abgeschlossn ist, wechselt das Programm in den "Running" Modus. Aber hier werden dann die Servowerte ausgewertet.

Ich habe den Quelltext entsprechend dokumentiert, sollten dennoch Fragen aufkommen, einfach posten.

Und nun noch etwas rechtliches (Winni, ich hoffe du erlaubst dass ich diesen Text von dir kopiert habe):

Den Code kann und darf jeder für sich ändern, solange es eine private Nutzung ist. Der Code wurde nur zum Einsatz in privaten Modellfahrzeugen erstellt. Jede andere Nutzung ist widerrechtlich. Gewerbliche Nutzung des Codes ist untersagt und wird rechtlich verfolgt. Eine gewerbliche Nutzung ist nur gegen Lizenzgebühr möglich. Es wird keine Haftung für Folgeschäden welche sich aus der Nutzung des Codes ergeben könnten übernommen. Der Einsatz erfolgt auf eigene Gefahr und eigenes Risiko. Eine Veröffentlichung in anderen digitalen oder Printmedien außerhalb dieses Forums ist nicht gestattet. Mit der Nutzung diese Codes erklärt sich der Nutzer hiermit einverstanden.

Code:
/*
    Name:       MyEKMFA.ino
    Created:	19.02.2019 18:16:46
    Author:     Peter Hartmann
*/
//	Wenn keine serielle Ausgabe erfolgen soll, dann dieses Define veraendern, z. B. in NO_SERIAL_OUTPUT
//	Dies spart Rechenleistung wenn man den Sketch in finaler Version einspielt.
#define SERIAL_OUTPUT

#pragma region Multiswitch Methoden

//	Speichervariablen fuer das Auslesen des Multiswitch Kanals
volatile	uint16_t		ReceivedMultiswitchValues[4]		= {1500, 1500, 1500, 1500};
volatile	uint16_t		ReceivedMultiswitchValuesSum		= 6000;
volatile	uint8_t			ReceivedMultiswitchValuesIndex		= 0;

volatile	uint32_t		LastChannelMultiswitchChange		= micros();
//	Diese Variable beinhaltet immerden zuletzt ermittelten Servowert in ms
volatile	uint16_t		ActualMeasuredMultiswitchValue		= 1500;

//	Variablen und Defines fuer die Realisierung eines Multiswitch Kanals

#define						MultiswitchStateUndefined					0
#define						MultiswitchStateCenter						1
#define						MultiswitchStateUp							2
#define						MultiswitchStateDown						3

//	Maximal- bzw. Minimalwert des Kanals. Zur Initialisierung werden diese mit festen Werten definiert, 
//	welche eine Standard Funke liefern koennen sollte. Bei Bedarf koennen diese auch angepasst werden.
			uint16_t		MeasuredMultiswitchUp						= 1950;
			uint16_t		MeasuredMultiswitchDown						= 1100;
//	Der Nullpunkt des Multiswitch Kanals wird beim Start ermittelt. Der hier festgelegte Wert ist eigentlich nicht notwendig.
			uint16_t		MeasuredMultiswitchCenter					= 1500;

//	Das Programm geht davon aus dass eine Bewegung des Knueppels (Tasters, Schalters, what ever) ueber die Mitte des moeglichen Weges
//	einen zaehlbaren Tippwert ergeben. Alle Bewegungen unterhalb dieser Mittelwerte werden nicht beruecksichtigt.
volatile	uint16_t		CalculatedMultiSwitchUpperStart				= ((MeasuredMultiswitchCenter + MeasuredMultiswitchUp) >> 1);
volatile	uint16_t		CalculatedMultiSwitchLowerStart				= ((MeasuredMultiswitchCenter + MeasuredMultiswitchDown) >> 1);

//	Die Zaehler fuer die Tipps nach oben bzw. nach unten
volatile	uint8_t			MultiswitchUpCounter;
volatile	uint8_t			MultiswitchDownCounter;
//	Der Zeitstempel wann das letzte Tippen gezaehlt wurde. Er wird im Programmablauf geprueft. Ist die letzte Tippaktion eine bestimmte Zeit her,
//	werden die bis dahin gezaehlten Tipps ausgewertet und die entsprechende Funktion ausgefuehrt. Danach werden die Counter wieder auf 0 gesetzt.
volatile	uint32_t		MultiswitchLastCounterChange;
//	Der Zustand waehrend der letzten Signalauswertung. Er wird gebraucht um ihn mit dem aktuellen Zustand zu vergleichen. Gibt es eine Aenderung
//	 muss reagiert werden.
volatile	uint8_t			MultiswitchLastState;

#define		ProcessingMode_Startup			1		//	Die Phase des Einmessens des Nullpunkts
#define		ProcessingMode_Running			2		//	Die eigentliche Programmausfuehrung

volatile	uint8_t			ProcessingMode								= ProcessingMode_Startup;

#define						NumberOfNeededValidSignals					10

volatile	uint8_t			StartupValidSignalCounter					= 0;

#pragma endregion

#pragma Multiswitch Funktionen

void InterruptMultiswitch( )
{
	uint32_t    nMicros				= micros();
	uint16_t    nDifference			= (uint16_t)(nMicros - LastChannelMultiswitchChange);

	LastChannelMultiswitchChange	= nMicros;

	if ((nDifference > 900) && (nDifference < 2000))
	{
		ReceivedMultiswitchValuesSum								-= ReceivedMultiswitchValues[ReceivedMultiswitchValuesIndex];
		ReceivedMultiswitchValues[ReceivedMultiswitchValuesIndex]	 = nDifference;
		ReceivedMultiswitchValuesSum								+= nDifference;
		ReceivedMultiswitchValuesIndex								 = ( ( ReceivedMultiswitchValuesIndex + 1 ) & 0x03 );	//	Index erhoehen und ggf. von 4 auf 0 springen
		nDifference													 = ( ReceivedMultiswitchValuesSum >> 2 );				//	durch 4 teilen

		ActualMeasuredMultiswitchValue								 = nDifference;

		if (ProcessingMode == ProcessingMode_Startup)
		{
			StartupValidSignalCounter++;
			return;
			//	Da wir uns im Startup Modus befinden ist eine weitere Bearbeitung des Signals nicht notwendig
		}
		uint8_t		NewMultiSwitchstate	= MultiswitchStateCenter;

		//	Nun wird aus dem Servowert berechnet ob sich der Kanal in der Mitte, oben oder unten befindet
		if (nDifference >= CalculatedMultiSwitchUpperStart)
			NewMultiSwitchstate	= MultiswitchStateUp;		//	Der Kanal ist oberhalb des oberen Schwellenwerts
		else
		{
			if (nDifference <= CalculatedMultiSwitchLowerStart)
				NewMultiSwitchstate	= MultiswitchStateDown;//	Der Kanal ist unterhalb des unteren Schwellenwerts
		}

		if ( MultiswitchLastState != NewMultiSwitchstate )
		{
			//	Der Wert unterscheidet sich vom vorherigen, entweder ist der Kanal von der Mitte in einen der ausseren gewechselt,
			//	oder er ist in die Mitte zurueck gekehrt.
			if (NewMultiSwitchstate == MultiswitchStateCenter)
			{
				//	Der Kanal ist aus einem der aeusseren Bereiche in die Mitte zurueck gewechselt.
				//	Diesen Status bearbeiten
				//	Der aktuelle Status muss ggf. in die Multiswitch Informationen als Auswahl eingepflegt werden
				switch(MultiswitchLastState)
				{
				case MultiswitchStateUp:
					//	Der Kanal ist aus dem oberen Bereich in die Mitte gewechselt.
					//	Der Zaehler fuer Tipps nach oben wird erhoeht und der fuer unten wird resettet
					MultiswitchUpCounter++;
					MultiswitchDownCounter	= 0;
					break;
				case MultiswitchStateDown:
					//	Der Kanal ist aus dem unteren Bereich in die Mitte gewechselt.
					//	Der Zaehler fuer Tipps nach unten wird erhoeht und der fuer oben wird resettet
					MultiswitchUpCounter	= 0;
					MultiswitchDownCounter++;
					break;
				}
				//	Zusaetzlich wird die Uhrzeit gemerkt wann dieser Wechsel statt gefunden hat.
				MultiswitchLastCounterChange	= millis();
			}
			//	Den letzten Status merken.
			MultiswitchLastState	= NewMultiSwitchstate;
		}
	}
}

void CalculateMultiSwitchSignalAreas()
{
	//	Der Mittelwert zwischen der Mitte und unten bzw. oben wird durch Addition der Mitte mit dem oberen bzw. unteren Wert und das anschließende 
	//	Teilen durch 2 errechnet.
	CalculatedMultiSwitchUpperStart				= ((MeasuredMultiswitchCenter + MeasuredMultiswitchUp) >> 1);
	CalculatedMultiSwitchLowerStart				= ((MeasuredMultiswitchCenter + MeasuredMultiswitchDown) >> 1);
#ifdef SERIAL_OUTPUT
	Serial.print(F("Lower: "));
	Serial.println(CalculatedMultiSwitchLowerStart);
	Serial.print(F("Upper: "));
	Serial.println(CalculatedMultiSwitchUpperStart);
#endif

}

void InitializeMultiswitchVariables()
{
	MultiswitchLastState				= MultiswitchStateCenter;

	MultiswitchLastCounterChange	= 0;
	MultiswitchUpCounter			= 0;
	MultiswitchDownCounter		= 0;

	//	Nun berechnen wir noch die Bereiche fuer den Multiswitch
	CalculateMultiSwitchSignalAreas();
}

void HandleMultiswitch()
{
	int	nMultiswitchCommand	= 0;

	if (MultiswitchLastCounterChange > 0)
	{
		//	Es liegt ein Zeitstempel eines Wechsel in die Mitte vor.
		//	Wenn die letzte Aenderung der Zaehlerstaende mehr als 750ms her ist, wird dies als das gewaehlte Kommando angesehen und gesetzt
		if (MultiswitchLastCounterChange < (millis() - 750))
		{
			//	Die Zaehler werden in einen Wert umgewandelt. Positive Werte sind Tipps nach oben, negative Werte sind Tipps nach unten
			if (MultiswitchUpCounter > 0)
			{
				nMultiswitchCommand		= (int)MultiswitchUpCounter;
				MultiswitchUpCounter	= 0;
			}
			else
			{
				if (MultiswitchDownCounter > 0)
				{
					nMultiswitchCommand		= -((int)MultiswitchDownCounter);
					MultiswitchDownCounter	= 0;
				}
			}
			//	Der Zeitstempel wird wieder geloescht, denn der Zaehlerwert wurde ja umgewandelt.
			MultiswitchLastCounterChange = 0;
		}
	}

	//	Auswertung des Multiswitch
	if (nMultiswitchCommand > 0)
	{
#if defined SERIAL_OUTPUT
		Serial.print(nMultiswitchCommand);
		Serial.println("x nach oben");
#endif
	}
	if (nMultiswitchCommand < 0)
	{
#if defined SERIAL_OUTPUT
		Serial.print(-nMultiswitchCommand);
		Serial.println("x nach unten");
#endif
	}

	//	Hier wird nun der errechnete Wert ausgewertet und die entsprechenden Funktionen ausgefuehrt.
	
	switch (nMultiswitchCommand)
	{
	case 1:		//	1x oben
		//	hier die entsprechende Aktion ausfuehren
		break;
	case -1:	//	1x unten	
				//	hier die entsprechende Aktion ausfuehren
		break;
	case 2:		//	2x oben
				//	hier die entsprechende Aktion ausfuehren
		break;
	case -2:	//	2x unten
				//	hier die entsprechende Aktion ausfuehren
		break;
	}
}

#pragma endregion


//	Diese Funktion auskommentieren wenn attachInterrupt() verwendet wird.
//  ------- von hier ---------
volatile	uint8_t			LastInterruptState					= PIND;

ISR(PCINT2_vect)
{
	uint8_t	PinState	= PIND;
	uint8_t	PinChanges	= PinState ^ LastInterruptState;

	if (PinChanges & (1<<PCINT18))
		InterruptMultiswitch();

	LastInterruptState	= PinState;
}
//	---------- bis hier -----------

// The setup() function runs once each time the micro-controller starts
void setup()
{

#ifdef SERIAL_OUTPUT
	Serial.begin( 115200 );
	Serial.println(F("Start..."));
#endif

	//	Den Interrupt initialisieren

	//	attachInterrupt(digitalPinToInterrupt(2), InterruptMultiswitch, CHANGE);
	//	Die folgenden 3 Zeilen auskommentieren wenn attachInterrupt() verwendet werden soll
	PCICR	|= (1 << PCIE2);
	PCMSK2	|= (1<<PCINT18);		//	Pin 2
	sei();
}

// Add the main program code into the continuous loop() function
void loop()
{

	if (ProcessingMode == ProcessingMode_Startup)
	{
		if (StartupValidSignalCounter >= NumberOfNeededValidSignals)
		{
			//	Es wurde die benoetigte Anzahl valider Signale erreicht. 
			//	Der bisher eingemessene Wert wird als der Nullpunkt des Kanals eingetragen
			MeasuredMultiswitchCenter	= ActualMeasuredMultiswitchValue;
			InitializeMultiswitchVariables();
			//	Umschalten auf den Standardmodus
			ProcessingMode				= ProcessingMode_Running;
#ifdef SERIAL_OUTPUT
			Serial.print(F("Measuring done..."));
			Serial.println(MeasuredMultiswitchCenter);
#endif
		}
	}
	else
	{
		//	Standardmodus
		// Um ein Einmessen des Kanals zu vermeiden wird der aktuelle Wert des kanals getestet. 
		//	Ist er groesser als der bekannte Grenzwert, wird dieser ausgeweitet und die Schwellenwerte 
		//	für den Multiswitch erneut kalibriert

		//	Diese Kalibrierung kann aber auch weg gelassen und feste Werte definiert werden.
		uint8_t	Calibrate = 0;
		if (ActualMeasuredMultiswitchValue < MeasuredMultiswitchDown)
		{
			//	Der Wert is groesser als der obere Grenzwert, also wird er als neuer oberster Grenzwert gesetzt und die Neuberechnung
			//	der Schwellenwerte angefordert
			MeasuredMultiswitchDown = ActualMeasuredMultiswitchValue;
			Calibrate				= 1;
		}
		if (ActualMeasuredMultiswitchValue > MeasuredMultiswitchUp)
		{
			//	Der Wert is kleiner als der untere Grenzwert, also wird er als neuer oberster Grenzwert gesetzt und die Neuberechnung
			//	der Schwellenwerte angefordert
			MeasuredMultiswitchUp	= ActualMeasuredMultiswitchValue;
			Calibrate				= 1;
		}
		if (Calibrate) CalculateMultiSwitchSignalAreas();

		//	Das Signal des Multiswitch auswerten und ggf. eine Funktion ausfuehren.
		HandleMultiswitch();
	}
	//	Dieser Delay ist OK, da ja jedes Servosignal 20ms lang ist, ein Wert kleiner 20
	//	wuerde also nur das bereits ausgewertete Signal erneut bearbeiten.
	delay(20);
}
 
Moin, moin...

Ich habe den Code mal auf einen ATTiny85 angepasst und ein Video gemacht...

Um diese Inhalte anzuzeigen, benötigen wir die Zustimmung zum Setzen von Drittanbieter-Cookies.
Für weitere Informationen siehe die Seite Verwendung von Cookies.
 

Servonaut
Zurück
Oben Unten