mrrwa / NmraDcc

NMRA Digital Command Control (DCC) Library
GNU Lesser General Public License v2.1
135 stars 53 forks source link

Wrong LEDs inNmraDccMultiFunctionMotorDecoder #58

Closed marko-pi closed 2 years ago

marko-pi commented 2 years ago

There an error is in NmraDccMultiFunctionMotorDecoder, lines 295-296

It should be

  digitalWrite(LED_PIN_FWD, newDirection ? HIGH : LOW);
  digitalWrite(LED_PIN_REV, newDirection ? LOW : HIGH);

instead of

  digitalWrite(LED_PIN_FWD, newDirection ? LOW : HIGH);
  digitalWrite(LED_PIN_REV, newDirection ? HIGH : LOW);

Forward corresponds to newDirection == HIGH.

devel-bobm commented 2 years ago

I would argue that either set of code can be "right" depending on how the LEDs are wired! One set of code is appropriate for "active low" LED control and the other set is appropriate for "active high" LED control. (I am too lazy to reverse-engineer the code to determine the sense of newDirection, so I cannot say for which the library's current code is appropriate!)

Which of these two approaches is appropriate depends on how you wire your LEDs to the device pins. And the library itself cannot predict this.

Here are a couple reasons why "active low" wiring may be appropriate:

Here are a couple of reasons why "active high" may be appropriate:

I would argue that perhaps the code-base could/should be changed to allow for a global variable that would establish whether function outputs were "active high" or "active low".

marko-pi commented 2 years ago

Thank you for the reply.

I was aware that the lamps are "commoned" to a positive voltage according to NMRA. However, your sketch is intended for a microprocessor running at 5V, so with common positive at 12V the only correct way to drive LEDs is with either transistor or (as in my case) MOSFET. [This is also beneficial, as I usually put two LEDs in series to get more luminous flow for the same electrical power or the same luminous flow for the smaller electrical power.] If someone uses NPN and N-channel MOSFET, the logic of the program will be wrong. If someone used PNP or P-channel MOSFET, the logic of the program would be correct. I am not an electrical engineer, but it seems to me that you cannot use PNP or P-channel MOSFET.

I do agree with you that this is obviously not a clear-cut situation, so you can disregard my report. Following the argumentation above, I intend to use inverse logic in my sketch anyway, despite it is based on your sketch.

P.S. I have rewritten the sketch to include all NMRA recommendations (acceleration, deceleration, signal timeout, manufacturer ID, emergency stop) and added CV120 for master reset. Shall I share my sketch on my github account or contribute to NmraDcc project?

devel-bobm commented 2 years ago

However, your sketch

The word "your" is quite ambiguous. I do not believe that I have contributed to the NmraDcc library nor its sketches. Others can claim that.

is intended for a microprocessor running at 5V

Also technically not true - the sketch is intended to be independent of microprocessor. Some users successfully use this sketch with microprocessors running at 3.3VDC, and I would not be surprised to find that some are using it with microprocessors running at 1.8VDC.

I would argue that "correct" is not the right word here. There are other methods. Probably the simplest method is to use a transistor or a MOSFET on each output. Some users find that integrated circuits with level-shifting "drive" transistors are a good solution. Others simply drive directly from the microcontroller output in "active low" mode and tie to +5VDC (for a non-NMRA-compliant solution). There are many ways to skin a cat, and there are many different applications, and many different users, and many different budgets.

If someone uses NPN and N-channel MOSFET, the logic of the program will be wrong.

No - there is no "right" and "wrong" here. Right/wrong depends on how the user is connecting his function loads, and the library/sketch cannot control that. But it can assume, and it has assumed a method that is opposite of what you choose to do. THAT DOES NOT make the library wrong! It does show that the library is inflexible.

I am not an electrical engineer, but it seems to me that you cannot use PNP or P-channel MOSFET.

I am an electrical engineer. It is possible to use a PNP or a P-channel MOSFET. One has to "jump through some hoops" to make it work by adding additional components. Or by using a transistor in a way that it is not very efficient. Or by paying more for more-expensive parts that suit such an application.

The logic is correct if there is no transistor between the microcontroller pin and the LED. I would argue that the vast majority of users of the library have simply connected an LED to an Arduino output pin, and it worked.

Remember, this librarary and the associated "sketches" are intended for experimenters. Users are encouraged to make use of the libraries and sketches "as-is", or to modify them to suit specific purposes.

If you have improvements to the library and/or sketches, they are welcomed.

Your desire for a library change to invert the sense of newDirection (and presumably other funciton outputs) would probably be best done as a configurable change. There are many ways to implement a change that is "configurable", and some are "better" than others. I know that a bunch of "configurable" changes have been made to the various MRRwA libraries, and I am sure that other contributors can give specific advice. (I am not a computer scientist and do not play one on TV. But I have submitted my fair share of well-intentioned but mis-directed software changes here and in other projects, so I am not the best person to give specific advice on this kind of software change!)

Remember that I am speaking as someone who has opinions, and someone who has some amount of control of what does/does not become part of the library. Others have opinions, and have differing levels of influence on the library. I am one voice among many.

I believe that the improvements you claim are desirable features for inclusion in MRRwA! But I wish to see them included in such ways that they do not simply break other people's implementations.

marko-pi commented 2 years ago
  • so with common positive at 12V the only correct way to drive LEDs is with either transistor or (as in my case) MOSFET.

I would argue that "correct" is not the right word here. There are other methods. Probably the simplest method is to use a transistor or a MOSFET on each output. Some users find that integrated circuits with level-shifting "drive" transistors are a good solution. Others simply drive directly from the microcontroller output in "active low" mode and tie to +5VDC (for a non-NMRA-compliant solution). There are many ways to skin a cat, and there are many different applications, and many different users, and many different budgets.

This is exactly my point: With Arduino/ATmega328/ATtiny and an NMRA-compliant solution, you can not drive directly from the microcontroller. Arguably, the simplest NMRA-compliant way (so simple that even I understand it) is to use an NPN-equivalent solution, and there the logic is exactly reversed. But as I wrote, I concede that this is not a clear-cut situation, so I give up my report/suggestion.

I was thinking of adding my new sketch to the NmraDcc library rather than replacing the existing sketch, but I see that this is so much trouble for both you and me that I'll post it as a standalone/unrelated sketch.

kiwi64ajs commented 2 years ago

Hi Marco,

On 29/08/2021, at 11:50 PM, marko-pi @.***> wrote:

There an error is in NmraDccMultiFunctionMotorDecoder, lines 295-296 It should be

digitalWrite(LED_PIN_FWD, newDirection ? HIGH : LOW); digitalWrite(LED_PIN_REV, newDirection ? LOW : HIGH); instead of

digitalWrite(LED_PIN_FWD, newDirection ? LOW : HIGH); digitalWrite(LED_PIN_REV, newDirection ? HIGH : LOW); Forward corresponds to newDirection == HIGH.

Hmmm… Staring at this for a bit - I think you’re right.

I see a bunch of other comments about Active High vs. Active Low etc but given the Off state is handled with:

digitalWrite(LED_PIN_FWD, LOW); digitalWrite(LED_PIN_REV, LOW);

I guess I’ve defined the logic as Active High and the direction values are:

typedef enum { DCC_DIR_REV = 0, /* The locomotive to go in the reverse direction / DCC_DIR_FWD = 1, /* The locomotive should move in the forward direction / } DCC_DIRECTION;

so newDirection = DCC_DIR_FWD = 1

which means your logic is right - I’ll change it in the code, so it goes out next release.

Thanks for spotting that. I probably had 2 LEDs side by side and was more focused on them changing vs. their orientation on the breadboard…

Regards

Alex Shepherd

m: +64-21-777764 e: @.***

kiwi64ajs commented 2 years ago

I've corrected this in 2.0.9, which I've just released - thanks