Pin numbers for unused pins.

I have written some programs for LoRa devices that are intended to run on a variety of Arduino boards.

Some LoRa modules may need an RXEnable and TXEnable pin driven, so the pin definitions for that board may be;

#define RXEnable A0 //RXEnable is on pin A0.
#define TXEnable A1 //TXEnable is on pin A1.

The pin are then enabled in startup(), pinmode(RXEnable, OUTPUT); etc. in the normal way.

Some LoRa modules may not need the RXEnable and TXEnable pins so to avoid needing to trap the pinmode(RXEnable, OUTPUT) command, I have been in the habbit of setting the #define to;

#define RXEnable -1 //RXEnable is not fitted or needed

The purpose of this is so that the program can be used with a variety of hardware without making changes to the main program. Any pins not present or needed were just defined as -1.

I dont recall why I started defining not present pins as -1.

I am beggining to suspect that defining these un-used pins as -1 is causing problems. In one case a Serial.println("Display Enabled") command appears on the serial monitor with a corrupt character like this;

Displùy Enabled

The use of the '#define RXEnable -1' appears to overwrite the area of RAM where 'Display Enabled' is stored.

In another case a program halts when there is a 'digitalWrite(RXEnable, HIGH); ' command.

In both cases changing the #define to #define SX1280_RXEN A0 and the programs work fine.

So the question is, is #define RXEnable -1 a valid command ?

So the question is, is #define RXEnable -1 a valid command ?

A #define is a preprocessor directive, not a command. The preprocessor will substitute the value wherever else the name appears in the code.

If RXEnable appears in a pinMode() call or a digitalWrite() call, and the value is -1, what you expect to happen, and what actually happens are likely two completely different things.

You really need a better approach, like testing that the value is non-negative, before using it. Of course, you really should NOT be using #define statements to "create" pin number "variables". Using const byte or const int is far better. A const byte can't hold a negative number...

the Arduino board packages (AVR, SAMD) check if the pin number is a valid pin in pin functions (pinMode, digitalWrite, ...) and do nothing if the pin number is not valid.
parameters for pin numbers are of type uint8_t so unsigned. -1 converts to 255

PaulS:
You really need a better approach, like testing that the value is non-negative, before using it. Of course, you really should NOT be using #define statements to "create" pin number "variables". Using const byte or const int is far better. A const byte can't hold a negative number...

Fair enough, but its not the use of #define that in itself causes the problem;

const int RXnable = -1;

Causes the same issue.

So is the use of -1 for a pin number prohibited ?

So is the use of -1 for a pin number prohibited ?

The various functions that take pin numbers check the validity of the pin number, and, hopefully, do the right thing if the number isn’t valid.

But, I prefer to do my own checking:

   if(RXEnable >= 0)
   {
      pinMode(RXEnable, someMode);
   }

You only have to type the code once.

I get the same 'RAM corruption' with;

const int SX1280_RXEN = 255;

But I dont get the error, or maybe just cannot see it, with;

const int SX1280_RXEN = 254;

Weird.

srnet:
I get the same 'RAM corruption' with;

const int SX1280_RXEN = 255;

But I dont get the error, or maybe just cannot see it, with;

const int SX1280_RXEN = 254;

Weird.

you forgot to mention what Arduino core has this problem

The avr core does NOT check the PIN number before using it. :frowning:
It tries to check for valid “port” AFTER translating the pin to a port... (rather dangerous)

Juraj:
you forgot to mention what Arduino core has this problem

Pro Mini 8Mhz, IDE 1.8.5.

westfw:
The avr core does NOT check the PIN number before using it. :frowning:
It tries to check for valid “port” AFTER translating the pin to a port... (rather dangerous)

So if the pin number is not valid, for the particualr board, and its then translated into an invalid port, there is the possibility of memory corruption ?

westfw:
The avr core does NOT check the PIN number before using it. :frowning:
It tries to check for valid “port” AFTER translating the pin to a port... (rather dangerous)

ArduinoCore-avr/Arduino.h at master · arduino/ArduinoCore-avr · GitHub

Slight correction. It does not check for a valid port, it checks that the returned value is not a specific value that was specified to denote invalid pins (the NOT_A_PIN value in Arduino.h).

This is much easier to code, but much more dangerous. It's very unlikely that accessing the -1 index of an array is going to return NOT_A_PIN exactly, so it passes that check, the value gets cast to a pointer, and who-knows-what gets written to god-knows-where and Bob's your uncle, as they say.

srnet:
So if the pin number is not valid, for the particualr board, and it's then translated into an invalid port, there is the possibility of memory corruption ?

Yep, exactly that.

Jiggy-Ninja:
This is much easier to code, but much more dangerous. It's very unlikely that accessing the -1 index of an array is going to return NOT_A_PIN exactly, so it passes that check, the value gets cast to a pointer, and who-knows-what gets written to god-knows-where and Bob's your uncle, as they say.Yep, exactly that.

Well thanks for confirming that.

It took me a couple of days to run down the problem, there is a fair bit of passing pointers to arrays to functions, I first thought I had made an error there.

Now I need to come up with a reliable method of having a pin ignored, if a particular board, does not have the pin in use. More thinking required I guess.

srnet:
Well thanks for confirming that.

It took me a couple of days to run down the problem, there is a fair bit of passing pointers to arrays to functions, I first thought I had made an error there.

Now I need to come up with a reliable method of having a pin ignored, if a particular board, does not have the pin in use. More thinking required I guess.

Conditional compilation with #defines is one option.

#define PIN_A 3

void setup()
{
#ifdef PIN_A
  pinMode(PIN_A, OUTPUT);
#endif

#ifdef PIN_B
  pinMode(PIN_B, OUTPUT);
#endif
}

Since PIN_B isn't defined, that second pinMode will just get erased.

PaulS:
A #define is a preprocessor directive, not a command.

I have learnt is in assembly language programming that a 'directive' is a command to the assembler; whereas, an instruction is a command to the processor. Should I change this notion of learning in view of the above quote?

GolamMostafa:
I have learnt is assembly language programming that a 'directive' is a command to the assembler; whereas, an instruction is a command to the processor. Should I change this notion of learning in view of the above quote?

official term is "preprocessor directive"

Jiggy-Ninja:
Since PIN_B isn't defined, that second pinMode will just get erased.

Yes, similar to what I thought to do.

Its does make the rest of the code look untidy however, whenever the 'unused pin' gets used in the code you need to do;

#ifdef PIN_A
digitalWrite(PIN_A, HIGH);
#endif

You can give arguments to #defines to make them into a kind of pseudo-function.

#define RXEnablePin  A0           //RXEnable  is on pin A0.
#define TXEnablePin  A1           //TXEnable  is on pin A1.

#ifdef RXEnablePin
 #define DriveRXEnable(x) digitalWrite(RXEnablePin, x)
#else
 #define DriveRXEnable(x)
#endif

#ifdef TXEnablePin
 #define DriveTXEnable(x) digitalWrite(TXEnablePin, x)
#else
 #define DriveTXEnable(x)
#endif

Basically, if the RXEnablePin is defined as something, DriveRXEnable will be replaced with the function it's been defined to. If it's not defined, the macro is defined to be blank so the "function call" doesn't exist anymore.