DCS-Skunkworks/dcs-bios-arduino-library

SwitchMultiPos not correctly accounting for PIN_NC and instead sends the PIN_NC value

kbastronomics opened this issue · 2 comments

In working with SwitchMultiPos we have a 8 position switch with pin 1 not connected.
when we switch between positions on the switch the not connected pin gets sent between each position (ie on a non-shorting switch)

so for example, say an 8 position switch and position 1 is not connected.
you start on position 2,
you move to position 3.
soon as position 2 disconnects position 1 (this PIN_NC pin) is sent so in DCS commands coming from the switch you see this
INS_SW 2
INS_SW 1
INS_SW 3

and if you continue sweeping the switch you get
INS_SW 1
INS_SW 4
INS_SW 1
INS_SW 5
INS_SW 1
INS_SW 6
INS_SW 1
INS_SW 7
INS_SW 1
INS_SW 8
INS_SW 1
INS_SW 7
and so on

from looking at the code for SwitchMultiPos

	class SwitchMultiPosT : PollingInput, public ResettableInput
	{
	private:
		const char* msg_;
		const byte* pins_;
		char numberOfPins_;
		char lastState_;
		bool reverse_;
		char readState() {
			unsigned char ncPinIdx = lastState_;
			for (unsigned char i=0; i<numberOfPins_; i++) {
				if( pins_[i] == PIN_NC)
					ncPinIdx = i;
				else
				{
					if (digitalRead(pins_[i]) == LOW && reverse_ == false) return i;
					else if (digitalRead(pins_[i]) == HIGH && reverse_ == true) return i;
				}
			}
			return ncPinIdx;
		}
		void resetState()
		{
			lastState_ = (lastState_==0)?-1:0;
		}
		void pollInput() {
			char state = readState();
			if (state != lastState_)
			{
				char buf[7];
				utoa(state, buf, 10);
				if (tryToSendDcsBiosMessage(msg_, buf))
					lastState_ = state;
			}
		}
	public:
		SwitchMultiPosT(const char* msg, const byte* pins, char numberOfPins, bool reverse = false) :
			PollingInput(pollIntervalMs),
			lastState_(0)
		{
			msg_ = msg;
			pins_ = pins;
			reverse_ = reverse;
			numberOfPins_ = numberOfPins;
			unsigned char i;
			for (i=0; i<numberOfPins; i++) {
				if( pins[i] != PIN_NC)
					pinMode(pins[i], INPUT_PULLUP);
			}
			lastState_ = readState();
		}
		
		void SetControl( const char* msg )
		{
			msg_ = msg;
		}
        
		void resetThisState()
		{
			this->resetState();
		}
	};
	typedef SwitchMultiPosT<> SwitchMultiPos;

in the public section you skip setting the pinMode for any PIN_NC which would seem to be correct.
then in the private section is where I beleive the issue to be

here is readState

		char readState() {
			unsigned char ncPinIdx = lastState_;
			for (unsigned char i=0; i<numberOfPins_; i++) {
				if( pins_[i] == PIN_NC)
					ncPinIdx = i;
				else
				{
					if (digitalRead(pins_[i]) == LOW && reverse_ == false) return i;
					else if (digitalRead(pins_[i]) == HIGH && reverse_ == true) return i;
				}
			}
			return ncPinIdx;
		}

you check if the pin is a PIN_NC and if so set ncPinIdx to the loop counter then return the the value of the loop count via return ncPinidx

now in Private pollInput()

		void pollInput() {
			char state = readState();
			if (state != lastState_)
			{
				char buf[7];
				utoa(state, buf, 10);
				if (tryToSendDcsBiosMessage(msg_, buf))
					lastState_ = state;
			}

you get the state which for the PIN_NC is going to be the ncPinidx value
since laststate was 2 and ncPinidx contatins 1 it will enter the if (state != lastState_) and then do the tryToSendDcsBiosMessage(msg_, buf) which we dont' want for a PIN_NC. we need it to not send a DcsBiosMessage in that case

one way around this would be to set ncPinidx to PIN_NC

		char readState() {
			unsigned char ncPinIdx = lastState_;
			for (unsigned char i=0; i<numberOfPins_; i++) {
				if( pins_[i] == PIN_NC)
					ncPinIdx = PIN_NC;
				else
				{
					if (digitalRead(pins_[i]) == LOW && reverse_ == false) return i;
					else if (digitalRead(pins_[i]) == HIGH && reverse_ == true) return i;
				}
			}
			return ncPinIdx;
		}

then in

		void pollInput() {
			char state = readState();
			if (state != lastState_)
			{
				char buf[7];
				utoa(state, buf, 10);
				if (tryToSendDcsBiosMessage(msg_, buf))
					lastState_ = state;
			}

check for PIN_NC and skip if that value is used

		void pollInput() {
			char state = readState();
			if (state != lastState_ || state != ncPinidx)
			{
				char buf[7];
				utoa(state, buf, 10);
				if (tryToSendDcsBiosMessage(msg_, buf))
					lastState_ = state;
			}

this way when state is PIN_NC the if loop is not entered so no DCS bios message is sent

my logic may be off in
void pollInput() {
char state = readState();
if (state != lastState_ || state != ncPinidx)
{
char buf[7];
utoa(state, buf, 10);
if (tryToSendDcsBiosMessage(msg_, buf))
lastState_ = state;
}

the idea is that a non-connected pin should not result in a tryToSendDcsBiosMessage being sent

maybe this approach is better

		void pollInput() {
			char state = readState();
			if (state != PIN_NC) {  // do not send dcs a message if a non connected pin
				if (state != lastState_)
				{
					char buf[7];
					utoa(state, buf, 10);
					if (tryToSendDcsBiosMessage(msg_, buf))
						lastState_ = state;
				}
			 lastState_ = state;  // not sure this is needed
			}
		}

looking thru history, I see why this was implemented this way as a default position for a multipostion switch
but it's had a side effect that when a non-shorting switch is used you get messages sent during the time the switch is between 2 positions which is not desirable.