Boat Automation Project

Hi all
I'm a bit new to this but have had my boat more or less automated on an arduino nano for the past year and just wanting to make everything a bit neater. I will break down the different parts of the project:

1:Power
The boat runs off 12V batteries which are divided into 2 banks (engine start and house) which are separated by a voltage sensitive relay. I want to design and implement a circuit in my project that will step down the voltage to 5V to run all my logic level stuff. This circuit will ideally clean up the power and have some protections in it.
2:Buttons Inputs
I am designing a circuit that allows at least 10 buttons to input into the nano using minimal io pins. Currently my plans are to use ic2 through a MCP 23017 chip.
3: LED Driver
Each button has a 12V LED ring around it and so I plan on using another MCP 23017 with 2 ULN 2803 transistor arrays to drive them. I am thinking about using a PWM pin to drive a mosfet on the shared positive line to dim the LEDs if they are too bright.
4:Relay Driver
All these buttons and LEDs are linked through the nano to a relay driver which will have a similar structure to the LED driver but power 16 power relays which will activate all the electrics on the boat

This is the basic structure. Other potential components include:
blown fuse indicator
Raspberry pi interface with arduino
Sensors for water level, fuel level, engine monitoring, fire, CO, Gas.

Once the basic circuits are designed I will probably need a bit of help with the programming as I have never used ic2. Let me know if this is the right place to post this. I was thinking of first posting all my basic circuit designs one at a time which I will breadboard and then make up with smd components.

Cheers
Patrick

First of all, it's I2C, not ic2. The wire library is the place to look for I2C functionality. Have you done some SMD design work before? What is your electronics background or skill level?

design and implement a circuit in my project that will step down the voltage to 5V

Buy, rather than build a suitable switching regulator. Pololu has the best selection.

sorry it took so long to get back. I am a doctor in the NHS and have no real coding or electronics background. I am reasonably handy though. I have the boat running on a temporary system using an Arduino nano and a number of parts bought online.

I don't want to continue just bolting pre-made parts together. The end goal of the project is not just to have an automated boat but to learn coding and electronics as I go. I only have a basic understanding currently from watching Youtube, tinkering etc. I have built a small workshop and have a benchtop power supply, soldering iron, bread and perf boards and a pile of rapidly increasing components. I like, as much as possible to understand the basic functionality of everything as I go along.

I am working on the buttons component currently and have a breadboard set up with a nano connected to a MCP23017. The buttons are all sinked to ground through a resistor and connected to the appropriate pins of the IC. The address pins are pulled to ground and the reset to high through a resistor. Power is supplied by the nano connected to the computer through usb.

Here is the code I have put together so far:

/*
 * Code for joining buttons onto a MCP23017 I/O expander.
 * Buttons are automatically initialised but must be attached
 * in sequential order starting GPA0-GPA7 (pin 21-28) then
 * GPB0-GPB7 (pin 1-8).
 */



#include <Wire.h>
#include "Adafruit_MCP23017.h"
Adafruit_MCP23017 mcp;

/* -------------------------------------------------------------
    set up global variables that can be adjusted
   -------------------------------------------------------------
*/

int numButtons =            9;    //number of buttons attached
const long longPressTime =  1000; //set time for a long press

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

void setup() {
  Serial.begin(9600); //initialise serial port
  mcp.begin();        // use default address 0 for mcp23017


  initialiseButtons (); //runs the initialise buttons function
  pinMode(LED_BUILTIN, OUTPUT);  // use the built-in LED for debugging
}



void loop() {
  digitalWrite(LED_BUILTIN, mcp.digitalRead(0));
  //the builtin LED will match button on pin (0)
                                                
  buttonPoll(); //run the buttonpoll function every loop to check 
                //for button presses
}

/* -------------------------------------------------------------
    function for setting all buttons to high and inputs
   -------------------------------------------------------------
*/
void initialiseButtons() {
  Serial.println("initialising...");
  for (int i = 0; i < numButtons; i++) { //for each button
    mcp.pinMode ((i), INPUT);            // set as input
    mcp.pullUp ((i), HIGH);              // set as high
    int n = (i + 1);
    Serial.print("button ");             //print button n initialised
    Serial.print(n);
    Serial.println(" initialised");
  }
}
/* -------------------------------------------------------------
    function for polling buttons for state change
   -------------------------------------------------------------
*/

void buttonPoll() {
  for (int i = 0; i < numButtons; i++) {//for each button
    int buttonPin = mcp.digitalRead (i);//check state
    if (buttonPin == LOW) {             //if state low
      int n = i + 1;
      Serial.print ("button ");         //print button n pressed
      Serial.print (n);
      Serial.println (" pressed");
      lengthPress (n);                  //launch lengthpress function
    }
  }
}


/* -------------------------------------------------------------
    function for detecting length of press (potential for slowing 
    code as while loop used so processing loops while button is 
    pressed)
    -------------------------------------------------------------
*/

void lengthPress(int n) {
  unsigned long buttonPressTime = 0;
  unsigned long pressStart = millis();
  int buttonState = LOW;
  unsigned long shortPressTime;
  int i = n - 1;

  while (buttonState == LOW) {
    buttonPressTime = (millis() - pressStart);//update the time the button has been pressed for
    buttonState = mcp.digitalRead (i); //update button state
  }
  Serial.print ("button ");
  Serial.print (n);
  Serial.println (" released");
if (buttonPressTime >= longPressTime){
   Serial.println ("longpress ");
}
else {
   Serial.println ("shortpress ");
}

}

It looks like this will work well for the buttons component of the project. Currently when I press any button my code detects it and prints to serial monitor which button is pressed, when it is released and if the press was long or short.

My concerns are

  1. using a while loop in the 'lengthPress' function may stop the processor from performing any other functions while a button is pressed. I don't yet know if this will cause any practical problems but I suspect it is not best form.
  2. this is an iteration after many others and in previous attempts with shift registers etc I needed to debounce the buttons. It no longer seems necessary, probably something to do with the IC and natural delays in the processing acting as a debounce. Will this have any problems?
    3)I have been exploring classes etc and wonder if there is not a more elegant way to arrange my code.
  3. now that I have the code detecting presses and their length I will need to design code that will perform actions based on it. I want to make it as easy as possible to read and to change the potential effect of pressing a button. Each button will be linked to a relay and an LED through the nano and function 1 (shortpress) could be to turn on/off the relay and the LED to indicate it is on. Function 2 (longpress) could be to turn on/off multiple relays and their associated LEDs or to turn one on for a set time and blink the led etc etc. I am struggling to build the code in, what I think is called, an object orientated approach to allow this.

Which part of the UK are you in?

Northern Ireland

Classes may be overkill for what you are doing. You can make things neat by using arrays. Create an array of function pointers that you will index by the number of the button that was pushed. When a button is pressed, invoke the corresponding function.

Thus, your code is somewhat data driven and when you want to change what a button does, you can change the function it calls in the array.

You can use the same method for the long press too.

Then you may realize that you could combine the two arrays into a single 2D array.

Perhaps too, there is some extra data that you will need associated with each function pointer. You might then evolve your design to an array of structs. And because a struct is much the same as a class, voila! You're using an object oriented design.

Thanks wildbill,

as I learn I find out more that I need to learn. I had come across arrays in passing and pointers through trying to understand other people's code. I will now need to spend some time looking into them in detail. I find the whole coding thing fascinating. It is causing me to think differently. I started with simple things like the blink tutorial and button tutorial and am slowly building my knowledge. In many iterations of my code I couldn't work out why things didn't work and it was normally because of the looping structure. I would regularly make updates that caused everything to come crashing down which led me to understand functions so that I could split my code up rather than putting it all in the loop.

I am currently building the LED driver circuit because it will be easier to debug the code if I see what it is doing

I have managed to put together an LED driver based off another mcp23017 and initialise it in code. It will be much the same as the button driver except in output rather than input. coding was a little tricky as i didn't know the address in the library but it seems that mcp1.begin(1) works fine.

I have a further driver for the relays to develop but with regards to code it will be identical to the led driver.

It will be reasonably easy now to develop the code to do the basic functions of press button > turn relay and led on/off

It may take longer to understand arrays, tidy the code and make it dynamic enough that I can adjust the actions as needed depending on button press time (short or long).

Then it is simply a matter of converting breadboards to perf boards and perf boards to PCB!

Could someone please advise if this is a sensible way to construct this function? The purpose of this function is to distinguish between a long and a short press of the button. As per the complete code in previous post this function is called when the buttonPoll function detects a state change in any of the buttons. The buttonPoll function is called in void loop and therefore runs regularly and if my code gets long I might call it at different stages in the loop. My concern with the below is that once the button press is detected I use a 'while' loop to detect release and count the time. I think this prevents the code from doing anything else and essentially if a user presses and holds a button (or an error causes the same) the code will be stuck at this point. I am not sure exactly how problematic this would be but it is not elegant.

I used this method because it allows me to retain the number of the button so that I can then link a function and doesn't get complicated if a user presses another button before the first has completed.

void lengthPress(int n) {
  unsigned long buttonPressTime = 0;
  unsigned long pressStart = millis();
  int buttonState = LOW;
  unsigned long shortPressTime;
  int i = n - 1;

  while (buttonState == LOW) {
    buttonPressTime = (millis() - pressStart);//update the time the button has been pressed for
    buttonState = mcp.digitalRead (i); //update button state
  }
  Serial.print ("button ");
  Serial.print (n);
  Serial.println (" released");
if (buttonPressTime >= longPressTime){
   Serial.println ("longpress ");
}
else {
   Serial.println ("shortpress ");
}

}

As you have realized, it's a blocking function and a press and hold on one of your buttons will lock things up until release. It's really up to you as to whether that matters though - do you need to respond to simultaneous button presses?

It is inelegant, but I would leave it as is for now and revisit it if you decide it's a significant problem.

At that time, you could create a couple of arrays (or an array of structs) with elements for each button, namely what the button state was when you read it and the time (millis) when you did. Then, whenever you detect button activity (press or release) on any button, you can look up what the state was, see what it is now and figure out if it had been pressed and for how long. You will need to have got your head around arrays for this though.

Thanks Wildbill, Arrays, structs, altdefs etc are on my current Youtube and internet searches. I will start to play with them and see how it helps me organise my code. Until things get much more complex it probably doesn't matter much and so far the coding has allowed me to develop the hardware to a level where I can get it off the breadboard.

OK, so I have been doing some research. I can make things work doing it without pointers and arrays but it is likely going to be a bit clunky, or at least look clunky. I am struggling to wrap my brain around the functionality of arrays and pointers even though I understand the concept. In my example, I need to read from a number of buttons then assign functions depending on the length of press which will include turning LEDs off and on or flashing them as well as turning relays off and on.

In the main loop I think I need to do 3 things

  1. Poll the buttons regularly to detect a state change
    2)Maintain the state of each LED
    3)Maintain the state of each Relay

This is because if a led is flashing it needs to be updated regularly and thus can not simply be controlled from a function outside the main loop without essentially creating a dead end in the code.

So, I think I need a call to a function in the main loop e.g ledsState(); in which to store the state of each LED.

I could write code for led1State = on or off or flash. Flash would be a function turning on or off as necessary according to timing. On or off would equate to a variable which is switched by the buttonpress function.

This seems OK but will look like a lot of duplicated code and I am sure arrays could help, I am just not sure how and my head hurts.

Any pointers (forgive the pun) would be appreciated.
I will code how I understand for now and then post it if I don't have any ideas.

Forget about pointers until you have mastered arrays. They are related, but you can ignore that for now.

If you are having trouble with arrays, I suggest that you create a cut down version of the requirements: Run one for three buttons, the associated leds and three relays.

You will likely end up with a set of variables called Led1State, Led2State and Led3State. There will be some like them for relays and buttons too. You will also have a lot of almost identical code but for the numbers.

Then add all the code for all the variables that have a 4 in them. That's the time that you will hopefully think: "There must be an easier way". There is and it's arrays.

To start with, you can just do a textual replacement. Declare your arrays and replace everything like Led1State with LedState[1]. I'm skimming over the fact that C arrays index from zero so the arrays will need to be one too big.

At this point, your code should still work, although it has gained you nothing. At that point you need to see the contextual leap that lets you use a for loop and an array to take all that hard coded relay code and collapse it into a single one. The other two areas are slightly harder.

Another thought: It would help me and probably you too to understand things better if you could document the requirement. Specifically, what each long and short presses are supposed to do and what they do to your LEDs and relays.

Thanks wildbill. I guess I am coming at this from multiple angles at once and it is probably not the best way to do code but it certainly helps me increment the hardware at the same time.

Basically the setup is:

I will have a panel of momentary buttons on the boat, each with an led ring around them. In simple form you press a button, the led turns on to indicate this and the mC activates the corresponding relay to, for example, turn on a cabin light. I can code this easily and already have it running. In fact the LEDs don't need logic they can be switched with the relays in this basic system.

However, I want additional functionality eg:
if I press and hold a cabin light button it will turn off all the lights not just the one corresponding to it. Or if I press and hold the gas solenoid button it will turn the gas on for e.g. an hour and the led will blink to show that a secondary function is in operation.

The secondary functions are all less important than the primary functions but are a) nice to have and b)help me learn code. I have not yet pinned down what they all will be.

I also have a Rasp Pi on the boat and eventually I want to have a node red (or similar) interface to allow me to control things from here or from a mobile phone. I want to be able to get alerts etc etc. So far, my knowledge and experience of both electronics and code is that they are both made up of quite simple things which, when all put together, can achieve quite remarkable things. I expect I will be fiddling with this system, upgrading and iterating for a long time.

I have ordered components for prototyping and am building the new system on a breadboard and I want to make sure that I don't restrict the coding potential in the hardware before I move on.

pmagowan:
I will have a panel of momentary buttons on the boat, each with an led ring around them. In simple form you press a button, the led turns on to indicate this and the mC activates the corresponding relay to, for example, turn on a cabin light. I can code this easily and already have it running. In fact the LEDs don't need logic they can be switched with the relays in this basic system.

One way to progress with arrays would be to take that running code and put leds, buttons and relay pins each into arrays and try to get it working again :wink:

ok, assume that the button press has been noticed, timed and converted into 3 cases
1 led is on and should be off
2 led is off and should be on
3 led should flash on and off at intervals

void leds (byte n, byte x) { //function with 2 variables to be passed, button number and function 0/1/2
  if (x = 2) {               // change x to flash function if equal to 2
    x = flash();
  }

  byte leds[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};  //led array of pins
  mcp1.digitalWrite (leds[n - 1], x);    //apply function to led pin, on/off/flash
}



int flash() {}

In my code (untested, for concept only) I create a function called 'leds'. I pass it a variable 'n' for the button number and another variable x for these cases. x can equal 0/1 or 2. If statement converts a 2 to another function which flashes the led on and off at intervals. 0 or 1 will write the associated LED to on or off. I digitalwrite to the array using -1 to 0 index.

Now, it seems a little pointless as I could just write: mcp1.digitalWrite (n-1,x) as the led pins run consecutively but I suppose this allows me to switch allocations more easily. Are there other benefits

also i need the flash to loop somehow or perhaps use the timer

edited to tidy code

Other benefits? Not in that example, although as you observe, there's value in being able to rearrange your pins.

What I was poking at is that for the basic functions, you read a button and toggle a relay and corresponding led. I suspect that right now, you have a bunch of hardcoded statements each of which handles one particular button. Arrays let you reduce that to one, smaller piece of code.

If however, there is special behaviour for some of the buttons, it may not be worth it.