This is what I've written. It's a handheld model railway control panel, with a pot, 4x4 keypad, OLED and nRF24L01 connected to an Uno. The pot controls loco speed and direction with a centre off setting (there's also a zero off option, but I'm removing that). The keypad numbers switch points, letters A to D set the route to the fiddle yard (staging yard) track, *[Letter] toggles track sections on or off (it's a DC layout), and *[Number] is for future use. Current data is displayed on the OLED.
This is the Tx, that doesn't have any output to the serial monitor, so I've attached a photo of the OLED display:
//*******KEYPAD*******
#include <Keypad.h>
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the cymbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {A2, A3, 6 , 7}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {2, 3, 4, 5}; //connect to the column pinouts of the keypad
//initialize an instance of class NewKeypad
Keypad keys = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
bool keyAlt = false;
//*******POT*******
int potPin = A1; // select the input pin for the potentiometer
int potVal = 0; // variable to store the value coming from the sensor
int potOldVal = 0;
//****This option not used, but variable needed as code still in place****
bool potCentreOff = true; //Use centre off, or full scale with separate direction switch
//***End of unused bit****
int potPWM = 0; //Pot value mapped to PWM range, using poCentreOff setting
char potDirection; //[F]orward, [R]everse, [S]top
//*******OLED*******
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
//*******DATA STRUCTURE*******
struct Tx_Data {
byte speed;
byte direction;
bool sections[4];
byte fyTrack;
byte point;
byte other;
} controlData;
bool dataChanged = false;
//*******STATUS*******
char statusFYTrack;
char statusSections[4];
//*******RADIO*******
#include <SPI.h>
#include "RF24.h"
byte addresses[][6] = {"HEtx1", "HErx1", "HErx2"};
RF24 radio(9, 10);
void setup() {
//*******OLED*******
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
display.clearDisplay();
display.setCursor(0, 10);
display.setTextColor(WHITE, BLACK);
display.setTextWrap(false);
display.setTextSize(3);
display.println(F("Holmans"));
display.setCursor(35, 40);
display.println(F("End"));
display.display();
delay(2000);
display.clearDisplay();
display.setTextSize(2);
display.drawFastHLine(0, 40, 128, WHITE);
display.display();
//*******RADIO*******
radio.begin();
radio.setPALevel(RF24_PA_LOW); //RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX (-18dBm, -12dBm,-6dBM, and 0dBm)
}
void loop() {
dataChanged = false;
//*******READ KEYPAD*******
char keyPressed = keys.getKey();
if (keyPressed) {
display.setCursor(0, 0);
display.print(F(" "));
display.setCursor(0, 0);
if (keyPressed == '*') {
keyAlt = true;
//display.setCursor(0, 0);
display.print(F("*"));
} else {
if (keyPressed == 'A' || keyPressed == 'B' || keyPressed == 'C' || keyPressed == 'D') {
if (keyAlt == true) {
switch (keyPressed) {
case 'A':
controlData.sections[0] = !controlData.sections[0];
dataChanged = true;
break;
case 'B':
controlData.sections[1] = !controlData.sections[1];
dataChanged = true;
break;
case 'C':
controlData.sections[2] = !controlData.sections[2];
dataChanged = true;
break;
case 'D':
controlData.sections[3] = !controlData.sections[3];
dataChanged = true;
break;
}
display.print(F("Section "));
display.print(keyPressed);
} else {
controlData.fyTrack = keyPressed;
dataChanged = true;
display.print(F("FY Track "));
display.print(keyPressed);
}
} else {
if (keyAlt == true) {
//controlData.other = keyPressed; //No other options currently available
//display.print("Other "); //No other options currently available
//display.print(keyPressed); //No other options currently available
display.print(F("!Unused!")); //No other options currently available
keyAlt = false;
} else {
if (keyPressed != '9' && keyPressed != '0' && keyPressed != '#') {
controlData.point = keyPressed;
dataChanged = true;
display.print(F("Point "));
display.print(keyPressed);
} else {
display.print(F("!Unused!"));
}
}
}
keyAlt = false;
}
display.display();
}
//*******READ POT*******
potVal = analogRead(potPin); // read the value from the sensor
if (potVal - 3 > potOldVal || potVal + 3 < potOldVal) {
//Centre off
if (potCentreOff == true) {
potPWM = 0;
potDirection = 'S';
if (potVal < 500) {
potPWM = map(potVal, 0, 500, 255, 0);
potDirection = 'R';
}
if (potVal > 523) {
potPWM = map(potVal, 523, 1023, 0, 255);
potDirection = 'F';
}
//Zero off
} else {
//****This section not currently used****
potPWM = map(potVal, 0, 1023, 0, 255);
if (potVal < 3) {
potPWM = 0;
potDirection = 'S';
}
//****End of unused section****
}
display.setCursor(0, 20);
if (potDirection == 'S') {
display.print(F("Stop "));
} else {
display.print(F("Speed: "));
display.setCursor(73, 20);
display.print(potDirection);
display.print(potPWM);
}
display.display();
controlData.direction = potDirection;
controlData.speed = potPWM;
dataChanged = true;
potOldVal = potVal;
}
//If acknowledged update status display
statusFYTrack = controlData.fyTrack;
//Add section switch status
display.setTextSize(1);
display.setCursor(0, 45);
display.print(F("FY Track: "));
display.print(statusFYTrack);
display.setCursor(0, 55);
display.print(F("Sections On: "));
display.setCursor(75, 55);
if (controlData.sections[0] == true) {
display.print(F("A "));
}
if (controlData.sections[1] == true) {
display.print(F("B "));
}
if (controlData.sections[2] == true) {
display.print(F("C "));
}
if (controlData.sections[3] == true) {
display.print(F("D "));
}
display.display();
display.setTextSize(2);
if (dataChanged) {
radio.openWritingPipe(addresses[0]);
//radio.openReadingPipe(1, addresses[1]);
//radio.stopListening();
if (!radio.write(&controlData, sizeof(controlData))) {
display.setCursor(0, 0);
display.print(F("!Tx Error!"));
display.display();
} else {
display.setCursor(120, 0);
display.print(F("*"));
display.display();
}
//radio.startListening();
//dataChanged = false;
}
}
Rx to follow, as the message was too long.