Programming additional pins as buttons

Hi all, brand new to arduino and programming. I am working on a project that controls the trim tabs on a boat via linear actuators. The below code has been created by a fellow member of a boat forum I am part of and works very well, however I am trying to add an additional switch to control one of the functions. So 2 switches controlling the one function. The idea being the switch can be operated by the driver or spotter at the back of the boat.

The first function called "surf" and it extends or retracts 2 x linear actuators based on pushing one side or the other of an on-off-on switch, the code has these switches going to pins 2 & 3 on the board. What I want to do is add 2 more pins to the function to receive a signal from a second switch that perform the same function as pins 2,3 within the code.

As I said I am brand new and cant figure it out It would be greatly appreciated if someone could tell me how to write this in.
The "launch" function works fine and needs no further adjusting.

Here is the current code.

#include <NMEAGPS.h>
#include <GPSport.h>
#include <EEPROM.h>

//RoMeo-specific motor controls
int E1 = 5;     //M1 Speed Control (left gate pin deploy)was 5
int E2 = 6;     //M2 Speed Control (right gate pin deploy) was 4
int M1 = 4;    //M1 Direction Control (left gate pin retract)was 6
int M2 = 7;    //M1 Direction Control (right gate pin retract) was 7


//Surf Settings - deploy speeds
float minSpeed = 6; // speed to deploy the tab when using GPS 
float maxSpeed = 17; // speed above which to retract tab when using GPS

// Launch Settings
int launchDeploy = 4500; // Time to Run tabs when launching (full extension)
int launchRetract = 4700; // time to retract tabs when done launching (full retract)
int planeSpeed = 20; // Speed at which your boat planes (tabs full retract in launch mode)
int launch = -1;
int launchbutton = 0;
// Actuator settings
// Default -- can be edited with Bluetooth app
int tabTime[ ] = {4500, 4500}; // The default amount of time in ms each tab to deploy

// Static values
const int retractTime[ ] = {5500, 5500}; // full time in ms to retract tabs fully from fully extended.
const int maxTime[ ] = {5400, 5400}; // the max amount of time an actuator should run
const int coolDown = 500; // Wait 500ms before taking a second action on same tab to prevent any issues




/// DO NOT EDIT BELOW HERE ////




// Pin Settings
// Switch pins -- wire with a ground and pin 2/3 as your surf left/right buttons on your switch.
const int buttonPins[ ] = {2, 3}; 

const int rotInterval = 100; // Time to increase or decrease tab deployment with each button push

// Relay control pins
const int directionGate[ ] = {4, 7}; // Specific to ROMEO Board JB
const int speedGate[ ] = {5, 6};

const int LEDPIN = 13;
const int LaunchPin = 12;
// GPS Hookup:
//TX- pin 8
//RX- Pin 9
//VCC- Pin 11
//Gnd- Pin 10

// ** No need to edit below here **


//GPS data
long lastUpdate = 0; //when the last update occurred
float lastSpeed = 0;
int satCount;
bool gpsOut;

// Timers
long deployTimers[ ] = { -1, -1}; // deploy timer -  0= left, 1= right for all arrays
long retractTimers[ ] = { -1, -1}; // retracting of actuators;
long coolDownTimers[ ] = { -1, -1};
int buttonStart[ ] = { -1, -1}; // start deploy time for buttons


long LEDTimer = 0;
long loopTimer; // a reusable loop timer.
int loopTime; // how long the full last loop took

// Misc
int i; // counter
bool speedlimit;
int start = 0;
int surf = -1; //off=-1, surf left=0, surf right=1
String tabMap[ ] = {"LEFT", "RIGHT"}; // just for printing out in serial
int deployed[ ] = {0, 0}; // how far tab is deployed
int batchMoveTime = 1000; // How many ms to wait after a move to take actoin -- this prevents repeated short bursts of time, with more accurate longer ones.


long lastRotUpdate;
boolean pendingMove = false;

NMEAGPS  gps; // This parses the GPS characters
gps_fix  fix; // This holds on to the latest values


char BLETest = 999;
void setup()
{
  Serial.begin(115200);         //*********JB Sets the data rate in bits per second (baud) for serial data transmission







  // Set the button pins to INPUT_PULLUP
  for (i = 0; i < 2; i = i + 1) {
    pinMode(buttonPins[i], INPUT_PULLUP);
  }
  pinMode (LaunchPin, INPUT_PULLUP);
  // LED pin
  pinMode(LEDPIN, OUTPUT);

  // Set the gates control pins to OUTPUT
  for (i = 0; i < 2; i = i + 1) {
    pinMode(directionGate[i], OUTPUT);
    pinMode(speedGate[i], OUTPUT);
  }

  // If we have any saved settings, restore them from EEPROM
  restoreSettings();

  // Enable GPS
  gpsPort.begin(9600);
 

}

//--------------------------///




void(* resetFunc) (void) = 0; //declare reset function @ address 0

void GPSloop()
{
  while (gps.available( gpsPort )) {
    fix = gps.read();
    lastUpdate = millis();
    lastSpeed = fix.speed_mph();
    satCount = fix.satellites;
  }
}

void saveSettings() {
  for (i = 0; i < 2; i = i + 1) { // loop thru to save
   
    EEPROM.write(i, tabTime[i] / rotInterval);
  }
}

void restoreSettings() {
  for (i = 0; i < 2; i = i + 1) { // loop thru to save
    int ee = EEPROM.read(i);
    if (ee > 1 and ee * rotInterval < maxTime[i]) {
      tabTime[i] = ee * rotInterval;
    } else {
      
    }
  }

  

}

void gpsLagCheck() {
  if ((millis() - lastUpdate) > 5000) {
    gpsOut = true;
  } else {
    gpsOut = false;
  }
}

void checkSpeed() {
  if (lastSpeed > minSpeed and lastSpeed < maxSpeed) {
    speedlimit = true;
  } else {
    speedlimit = false;
    surf = -1;

  }
}

void LaunchGo()
   { analogWrite(E1, 255);
      analogWrite(E2, 255);
      digitalWrite(M1, HIGH);
      digitalWrite(M2, HIGH); //deploy tabs
      Serial.print("F");
      delay(launchDeploy); //deploy for set time in parameters
      { analogWrite(E1, 0);
        analogWrite(E2, 0);
      } BLETest = -1;
      launch = 1;
    }

    
void LaunchAbort()
 {   analogWrite(E1, 255);
      analogWrite(E2, 255);
      digitalWrite(M1, LOW);
      digitalWrite(M2, LOW); //retract tabs
      delay(launchRetract); //retract for set time in parameters
      { analogWrite(E1, 0);
        analogWrite(E2, 0);
      }//stop deploy}
      Serial.print("G");
      launch = -1;
    }

void deployTab(int tab, int start) {

  for (i = 0; i < 2; i = i + 1) { // loop thru to see if we need to retract anything
    if (i != tab and (deployed[i] > 0 or deployTimers[i] > 0)) {
      retractTab(i, 0);
    }
  }
  retractTimers[tab] = -1;
  analogWrite(speedGate[tab], 255);
  digitalWrite(directionGate[tab], HIGH);
  deployTimers[tab] = millis() - start;
  
}

void retractTab(int tab, int start) {
  deployTimers[tab] = -1;
  analogWrite(speedGate[tab], 255);
  digitalWrite(directionGate[tab], LOW);
  if (start != 0) {
    retractTimers[tab] = millis() - (tabTime[surf] + start);
  } else {
    retractTimers[tab] = millis();

  }
  
}

void resetTabs() {
  for (i = 0; i < 2; i = i + 1) { // loop thru both
    deployTimers[i] = -1;
    deployed[i] = 0;
    coolDownTimers[i] = -1;
    retractTab(i, 0);
  }
}
void goSurf() {

  if (surf > -1) { // Is surf enabled?  surf 0=left surf1=right

    if (speedlimit == 1) { // Are we in a deploy speed range
      if (deployTimers[surf] == -1 and retractTimers[surf] == -1 and deployed[surf] < 1 and (millis() - coolDownTimers[surf]) > coolDown) {
        deployTab(surf, 0);
      }
    }

    // Check if we have anything to retract
    if (speedlimit == 0) { // Are we outside of a deploy speed range
      for (i = 0; i < 2; i = i + 1) { // loop thru both to see if we need to retract
        if (retractTimers[i] == -1 and deployTimers[i] == -1 and deployed[i] > 1 and (millis() - coolDownTimers[surf]) > coolDown) {
          retractTab(i, 0);
        }
      }
    }

  } else { //  auto-retract when surf is off
    for (i = 0; i < 2; i = i + 1) { // loop thru both to see if we need to retract
      if (retractTimers[i] == -1 and deployTimers[i] == -1 and deployed[i] > 1 and (millis() - coolDownTimers[surf]) > coolDown) {
        retractTab(i, 0);
      }
    }
  }
}


void tabTimers() {
  // Check timers for status
  for (i = 0; i < 2; i = i + 1) {
    if (deployTimers[i] > 0 and (millis() - deployTimers[i]) > tabTime[i] and retractTimers[i] == -1) {
      deployed[i] = tabTime[i];
      //Do your deploy complete logic here, IE: set pins

      Serial.print("H");
     analogWrite(speedGate[i], 0);

      deployTimers[i] = -1;
      coolDownTimers[i] = millis();
    }
  }
  for (i = 0; i < 2; i = i + 1) {
    if (i != surf) {
      if (retractTimers[i] > 0 and (millis() - retractTimers[i]) > retractTime[i] and deployTimers[i] == -1) {
        deployed[i] = 0;

        //Do your retract complete logic here, IE: set pins
        analogWrite(speedGate[i], 0);
        Serial.print("I");

        retractTimers[i] = -1;
        coolDownTimers[i] = millis();
      }
    } else {
      // Do adjust here instead of full retract

      if (retractTimers[i] > 0 and millis() > retractTimers[i] and (millis() - retractTimers[i]) > tabTime[i] and deployTimers[i] == -1) {

        deployed[i] = tabTime[i];

        //Do your retract complete logic here, IE: set pins
        analogWrite(speedGate[i], 0);
        retractTimers[i] = -1;
        coolDownTimers[i] = millis();
      }
    }
  }
}
  

void Buttoncheck() {
  for (i = 0; i < 2; i = i + 1) {
    int buttonValue = digitalRead(buttonPins[i]);
    if (buttonValue == HIGH and surf == i and surf != -1)
    {
      if (buttonStart[i] == -1) {
        buttonStart[i] = millis();
      }
      if (millis() - buttonStart[i] > 1000) {
        surf = -1;
        buttonStart[i] = -1;
      }
    }
delay(1); //COMMENT BACK IN IF HAVING INTERFERENCE ISSUES
    if (buttonValue == LOW and surf != i) {
      if (buttonStart[i] == -1) {
        buttonStart[i] = millis();
      }
      if (millis() - buttonStart[i] > 1000) {
        surf = i;
        buttonStart[i] = -1;
      }
    }
  }
  delay(1); //COMMENT BACK IN IF HAVING INTERFERENCE ISSUES
}

void Bluetooth() {
  if (Serial.available() > 0) // Send data only when you receive data:
  {
    BLETest = Serial.read();      //Read the incoming data and store it into variable data
    if (BLETest == '1')   //Checks whether value of data is equal to 1
    {
      lastRotUpdate = millis(); //trick into thinking rotary haas been updated
      pendingMove = true; //tell control a movement is coming
    { if (tabTime[surf] < maxTime[surf]  ) {
          tabTime[surf] = tabTime[surf] + 100;
          Serial.print("A");
        } //add 100ms to tab time
        else {
          Serial.println(F("MAX"));
        } //Print max and don't allow further extension
      }
    }

    else if (BLETest == '0')      //Checks whether value of data is equal to 0
    {
      lastRotUpdate = millis();
      pendingMove = true;
    { if (tabTime[surf] > 0 ) {
          tabTime[surf] = tabTime[surf] - 100;
          Serial.print("B");
        } // subtract 100ms from tab time
        else {
          
        }//Print min and don't allow further retraction
      }
    }

    else if (BLETest == '3')
    {
      Serial.print("C");
      resetFunc();
    }

    else if (BLETest == '4')
    {
      Serial.print("D");
      resetTabs();
    }

    else if (BLETest == '5')
    {
      Serial.print("E");
      saveSettings();
    }
    else if
    (BLETest == '6' and surf == -1 and lastSpeed < planeSpeed)   //Checks whether value of data is equal to 6, surf is off, and speed is under plane speed set in parameters
    { LaunchGo();}

    
    else if (BLETest == '7' and surf == -1)   //Checks whether value of data is equal to 7, surf is off
    { LaunchAbort();}
    
    else if (BLETest == '8')
    {
      Serial.print("J");
      minSpeed = -1;
    }
    else if (BLETest == '9')
    {
      Serial.print("K");
      minSpeed = 7;
    }
  }


  //check to make sure we should be able to move the tab and bunch multiple clicks into one move
  if (millis() > lastRotUpdate + batchMoveTime and tabTime[surf] != deployed[surf] and deployTimers[surf] == -1 and retractTimers[surf] == -1 and pendingMove) {
    if (tabTime[surf] > deployed[surf]) {
      deployTab(surf, deployed[surf]);
    } else {
      retractTab(surf, tabTime[surf] - deployed[surf]);
    }
    pendingMove = false;
  }

}
void ButtonLaunch()
{launchbutton = digitalRead(LaunchPin);
if (launchbutton == LOW and surf == -1 and lastSpeed < planeSpeed and launch == -1)   //Checks whether button has been pushed, surf is off,speed is under plane speed set in parameters, and not currently launching
 LaunchGo();

else if (launchbutton == LOW and surf == -1 and lastSpeed < planeSpeed and launch == 1)
 LaunchAbort();
}
void OnPlaneCheck()
{ if (lastSpeed > planeSpeed and launch == 1)
  { analogWrite(E1, 255);
    analogWrite(E2, 255);
    digitalWrite(M1, LOW);
    digitalWrite(M2, LOW); //retract tabs
    delay(launchRetract); //retract for set time in parameters
    { analogWrite(E1, 0);
      analogWrite(E2, 0);
    }//stop deploy}
    Serial.print("G");
    launch = -1;
  }
}
void surfSideSerial()
{
  if (surf == 0)
    Serial.print("L");
  else if (surf == 1)
    Serial.print("R");
  else if (surf == -1)
    Serial.print("N");
}


void loop()
{
  loopTime = millis() - loopTimer;
  loopTimer = millis();
  tabTimers();
  Buttoncheck(); //Comment back in to use hard buttons
  ButtonLaunch();
  checkSpeed();  // Check  if my speed is in the deploy range
  OnPlaneCheck(); //retract tabs if on plane
  Bluetooth ();
  //minSpeed = -1;
  GPSloop(); // Get GPS data
  goSurf();
 



}
  • First things first, please post your code properly.

  • In the Arduino IDE, use Ctrl T or CMD T to format your code then copy the complete sketch.
    Use the < CODE / > icon from the ‘posting menu’ to attach the copied sketch.

  • Did you write this or did AI write this ?

the output from an RC receiver is Servo Control pulse that drives an RC servo.

It's not the same as a switch connected to an Arduino pin

Please specify make and model or a link to where you purchased this.

a7

Let me clarify, the wireless receiver also has relays, so when I push a button on the transmitter it just closes or opens the circuit on the relay, which is the same as the standard switch does.

Like I said brand new here. I am not aware that AI was used to write this, it was shared on another forum. Do you have an answer for me or did you feel you just needed to put in your 2 bob to set the uninitiated straight on how things should be.

  • Neither do I, that’s why the question was ask.

  • There are posting guide lines at the top of every forum that you should review before asking questions.
    Following these guide lines helps us better help you with your project.

  • We are all volunteers here, we try are best to help newcomers who come to the forums for help.
    There is no need for you to become ruffled, or maybe you are referring to my bob haircut :wink:

Sure I understand that. Anyway I tried editing and posting the code as you suggested. Have no idea if it worked though. As for the AI question, the guy who wrote it may had had some help. It was done in 2020 so I don't know AI was there for the general public. Does it make a difference if it was. As I said it works completely fine and many other boaters like myself have used it to automate the tabs on their boats.

ok. so in other words, 2 pins now control each of those 2 functions. However, i think the existing buttonCheck() is awkward.

i think the following code could easily handle multiple button pins and each function would be executed for 2 cases

// check multiple buttons and toggle LEDs

enum { Off = HIGH, On = LOW };

byte pinsLed [] = { 10, 11, 12 };
byte pinsBut [] = { A1, A2, A3 };
#define N_BUT   sizeof(pinsBut)

byte butState [N_BUT];

// -----------------------------------------------------------------------------
int
chkButtons ()
{
    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        byte but = digitalRead (pinsBut [n]);

        if (butState [n] != but)  {
            butState [n] = but;

            delay (10);     // debounce

            if (On == but)
                return n;
        }
    }
    return -1;
}

// -----------------------------------------------------------------------------
void
loop ()
{
    switch (chkButtons ())  {
    case 2:
        digitalWrite (pinsLed [2], ! digitalRead (pinsLed [2]));
        break;

    case 1:
        digitalWrite (pinsLed [1], ! digitalRead (pinsLed [1]));
        break;

    case 0:
        digitalWrite (pinsLed [0], ! digitalRead (pinsLed [0]));
        break;
    }
}

// -----------------------------------------------------------------------------
void
setup ()
{
    Serial.begin (9600);

    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        pinMode (pinsBut [n], INPUT_PULLUP);
        butState [n] = digitalRead (pinsBut [n]);
    }

    for (unsigned n = 0; n < sizeof(pinsLed); n++)  {
        digitalWrite (pinsLed [n], Off);
        pinMode      (pinsLed [n], OUTPUT);
    }
}

Thanks much appreciated. I couldn't tell if code was good or bad. I believe the person who wrote the code was much like myself and just muddled his way through it. AlI I know it works as quite a few people have use it to build their own controllers for their boats with a lot of success, I have no doubt that an expert would do it in a much more pragmatic way. I will try adding what you have done and see how it goes. Thanks again.

not clear what ButtonCheck() is intended to do.

looks like set surf to the button index, 0 or 1, after being pressed for 1 sec and then sets surf to -1 after the button is releaded for 1 sec.

If you have regular pushbuttons, and a NO set of contacts each relay, you could just wire those contacts across the switch terminals.

a7

2 Likes

Yes, it worked.

I second the idea of putting the new contacts in parallel with the existing switch. The only caution is to not try to run both switches at the same time. No code changes needed.

I'm using 3 way rocker switches, push up on the rocker switch and one tab extends and the other retracts, pust down and it reverses the previous. I thought about just doing this outside of the board and wiring the second switch directly to the relays but I wanted to be able to use the deploy and retract times in the code so I don't have to guess what position the tabs are in when I stop them from moving.