Bait boat on arduino

The assembly of a radio controlled bait boat began as part of my first student project on arduino. I lived far from the bustle of the city, so I had to basically use only those components that were on hand. The task was simple – to create a boat that could deliver fish feed with a payload of about two kilograms. To achieve my goals, I had to solve a list of the following tasks:

  • Make a body and determine the dimensions.

  • Make movers.

  • Select an engine.

  • Provide radio control.

  • Ensure power supply.

  • Compiling it all.

The next step broke the whole thing into an electronic part and a mechanical one. The mechanical part was supposed to decide the movement of the boat on the water, and the electronic part was supposed to control the engines and provide communication between the control unit and the remote control. According to this logic, a simple block diagram was drawn.

Structural scheme

Structural scheme

Mechanical

For the manufacture of the hull, I downloaded about ten books on shipbuilding and ship modeling and began to study them. As a result, I realized that the technical one could not do the things that were written in these books. For the manufacture of frames, a CNC router or a 3D printer was needed. Unfortunately, I didn’t have them. Doing all this with your hands from wood is also not an easy task. In the end, I settled on the option with a catamaran, as I found many analogues on the Internet. Decided to do it all from improvised means. I was looking for something streamlined, lightweight and found a solution in my garage. Yes, these were oil cans and a tool case. I glued them with thermo-glue and, for reliability, reinforced with wood and fixed with a self-tapping screw. Painted in different colors to be seen from afar.

boat hull

boat hull

Then there were unsuccessful attempts to calculate the hydrodynamic characteristics on the FlowVision CFD. As a result, I stopped at the stage of 3D modeling =)

3D model for Compass

3D model for Compass

Generated drawing for Compass

Generated drawing for Compass

The propeller was chosen based on the efficiency indicator and the best in this regard, of course, was the underwater propeller. The screw consists of a hub and blades radially fixed to it. Usually 3-4 blade propellers are used. All propeller sizes were calculated according to the book “Young Model Shipbuilder”. The screw was made of a steel plate, the shaft was made of a pvc pipe, and the inside was filled with ordinary grease.

Homemade mover

Homemade mover

The situation is similar with the engine. I didn’t have brushless ones, they went to and from China for a very long time. I found somewhere and installed collector 20 watt motors from the VAZ ME 255 stove.

ME 255

ME 255

To move the feed ejection mechanism, I used a Starline SL-2 two-wire electric drive from the machine lock.

Starline SL-2

Starline SL-2

Electronic part

Let’s move on to the electronic part. The Chinese Arduino Nano 3.0 based on Atmega328P was chosen as the brain of all this, since its computing power was enough from stocks and it is easy to prototype on it. Then I searched the Internet for similar projects for different tasks and found an interesting control unit project on youtube, borrowed some of the code and circuits from there and adapted it to my tasks. The radio module used NRF24L01+PA+LNA. In an open area, it was possible to send packets to 700m with it. The L298N was used as an electric motor driver, although it got very hot due to the fact that it was not designed for these motors (it is better to take it with a margin), I had to strengthen their radiators with radiators from old TVs. The control panel worked on the same bundle plus 2 KY-023 modules.

Remote Control

Functional diagram of the control panel

Functional diagram of the control panel

Schematic diagram of the control panel (after completion, the battery was replaced by 7.4 V and a Schottky diode was added to protect against polarity reversal)

Schematic diagram of the control panel (after completion, the battery was replaced by 7.4 V and a Schottky diode was added to protect against polarity reversal)

Control panel layout

Control panel layout

Control panel assembly

Control panel assembly

Finished remote after assembly

Finished remote after assembly

Main unit

Functional diagram of the main unit

Functional diagram of the main unit

Arduino and radio module

Arduino and radio module

Arduino and motor drivers

Arduino and motor drivers

Model with one motor for testing

Model with one motor for testing

Arduino and radio module (plus an additional driver for small motors if necessary)

Arduino and radio module (plus an additional driver for small motors if necessary)

Schematic diagram of the control board of the feed ejection mechanism

Schematic diagram of the control board of the feed ejection mechanism

Feed release board layout

Feed release board layout

Board after surface mounting

Board after surface mounting

Schematic diagram for the board of additional loads (serial diodes were needed to lower the voltage, they can be removed)

Schematic diagram for the board of additional loads (serial diodes were needed to lower the voltage, they can be removed)

Payment for additional loads

Payment for additional loads

Reinforcement of radiators

Reinforcement of radiators

Installation of engines.  Battery connection.  Installation of the motor for the reset mechanism.  Feed bin.

Installation of engines. Battery connection. Installation of the motor for the reset mechanism. Feed bin.

Screw and screw protection.  Reset mechanism.  Electronics installation.

Screw and screw protection. Reset mechanism. Electronics installation.

Code for the main block (rx.ino)

#include <SPI.h> // Подключаем библиотеку для работы с SPI
#include <nRF24L01.h> // Подключаем библиотеку для работы с радиомодулем nRF24L01
#include <RF24.h> // Подключаем библиотеку для работы с радиомодулем nRF24L01

const uint64_t pipe = 0xE8E8F0F0E1LL; // Указываем адрес конфигурации радиомодуля для обмена данными
RF24 radio(2, 9); // Инициализируем объект радиомодуля на пинах 2 и 9

int data[4]; // Создаем массив для хранения полученных данных
int reserve=0; // Инициализируем переменную для хранения резервной кнопки, использовать при необходимости
unsigned long motorOnTime; // Инициализируем переменную для хранения времени включения двигателя
byte count = 0; // Инициализируем переменную счетчик для механизма сброса

// В функции setup инициализируются различные пины как входы или выходы, устанавливается канал радио, скорость передачи данных и мощность.
void setup() {
  delay(50); // Небольшая задержка перед началом работы
  radio.begin(); // начало работы с радиомодулем
  radio.setChannel(9); // установка радиоканала
  radio.setDataRate(RF24_250KBPS);        // Установка минимальной скорости;
  radio.setPALevel(RF24_PA_HIGH);         // Установка максимальной мощности;
  radio.openReadingPipe(1,pipe);  // Открытие канала для чтения данных
  radio.startListening(); // Начало прослушивания канала
  pinMode(10, OUTPUT); // Установка пина 10 на вывод
  digitalWrite(10, LOW); // Установка пина 10 в низкий уровень
  pinMode(17, OUTPUT); // Установка пина 17 на вывод
  pinMode(18, OUTPUT); // Установка пина 18 на вывод
  pinMode(19, OUTPUT); // Установка пина 19 на вывод
  pinMode(16, OUTPUT); // Установка пина 16 на вывод
  digitalWrite(16, LOW); // Установка пина 16 в низкий уровень
}

void loop()  {
  if ( radio.available() ){ // Если доступны новые данные от радиомодуля
    bool done = false;
    while (!done){ // Читаем данные, пока все данные не будут прочитаны
      done = radio.read(data, sizeof(data)); // Считываем данные в массив data размером sizeof(data)
      // Проверка полученных данных от левого джойстика (data[0]), управление левым двигателем
      if(data[0]>450 && data[0]<598){ // Если значение элемента массива в заданном диапазоне
        analogWrite(5, 0); // Отключение левого двигателя
        analogWrite(6, 0); // Отключение левого двигателя
        digitalWrite(17,LOW); // Установка пина 17 в низкий уровень
        digitalWrite(10,LOW); // Установка пина 10 в низкий уровень
        digitalWrite(18,LOW); // Установка пина 18 в низкий уровень
        digitalWrite(19,LOW); // Установка пина 19 в низкий уровень
      }
      if(data[0]>598)
      {   
        analogWrite(5,255); // Включение левого двигателя
        analogWrite(6,255); // Включение левого двигателя
        digitalWrite(17,LOW); // Установка пина 17 в низкий уровень
        digitalWrite(10,HIGH); // Установка пина 10 в верхний уровень, направление движения вперед 
        digitalWrite(18,HIGH); // Установка пина 10 в верхний уровень, направление движения вперед
        digitalWrite(19,LOW); // Установка пина 19 в низкий уровень
      }
      if(data[0]< 450)
      {   
        analogWrite(5,255); // Включение левого двигателя
        analogWrite(6,255); // Включение левого двигателя
        digitalWrite(17,HIGH); // Установка пина 17 в верхний уровень, направление движения назад 
        digitalWrite(10,LOW); // Установка пина 10 в низкий уровень
        digitalWrite(18,LOW); // Установка пина 18 в низкий уровень
        digitalWrite(19,HIGH); // Установка пина 19 в верхний уровень, направление движения назад
      }  
      // Проверка полученных данных от правого джойстика (data[1]), управление правым двигателем
      if(data[1]>450 && data[1]<598){ // Если значение первого элемента массива в заданном диапазоне
        digitalWrite(3, LOW); // Отключение правого двигателя
        digitalWrite(8, LOW); // Отключение правого двигателя
        digitalWrite(4,LOW); // Установка пина 4 в низкий уровень
        digitalWrite(7,LOW); // Установка пина 7 в низкий уровень
        digitalWrite(14,LOW); // Установка пина 14 в низкий уровень
        digitalWrite(15,LOW); // Установка пина 15 в низкий уровень
      }
      if(data[1]>598)
      {   
        digitalWrite(3, HIGH); // Включение правого двигателя
        digitalWrite(8,HIGH); // Включение правого двигателя
        digitalWrite(4,LOW); // Установка пина 4 в низкий уровень
        digitalWrite(7,HIGH); // Установка пина 7 в верхний уровень, направление движения вперед 
        digitalWrite(14,HIGH); // Установка пина 14 в верхний уровень, направление движения вперед 
        digitalWrite(15,LOW); // Установка пина 15 в низкий уровень
      }
      if(data[1]< 450)
      {   
        digitalWrite(3,HIGH); // Включение правого двигателя
        digitalWrite(8,HIGH); // Включение правого двигателя
        digitalWrite(4,HIGH); // Установка пина 4 в верхний уровень, направление движения назад
        digitalWrite(7,LOW); // Установка пина 7 в низкий уровень
        digitalWrite(14,LOW); // Установка пина 14 в низкий уровень
        digitalWrite(15,HIGH); // Установка пина 15 в верхний уровень, направление движения назад
      }  
      // Проверка полученных данных от кнопки правого джойстика (data[2]), управление механизмом сброса
      if(data[2] == 1){ // Если равно 0
         if(count < 1){ // Счетчик меньше единицы
            digitalWrite(16, HIGH); // Замыкаем реле запускаем механизм сброса
            delay(500); Ждем 500 миллисекунд
            digitalWrite(16, LOW); // Отключаем
            count++; // Увеличиваем счетчик на единицу 
         }
      }
      else{
        if(count >= 1){ // Если счетчик равно или больше единицы
            digitalWrite(16, HIGH);  // Замыкаем реле запускаем механизм сброса
            delay(500); Ждем 500 миллисекунд
            digitalWrite(16, LOW); // Отключаем
            count--; // Уменьшаем счетчик на единицу 
         }
      }
      reserve = !data[3]; // Резервная кнопка правого джойстика для свободного пользования
    }
  }
}

Code for control panel (tx.ino)

#include <SPI.h> // Подключаем библиотеку для работы с SPI
#include <nRF24L01.h> / Подключаем библиотеку для работы с радиомодулем nRF24L01
#include <RF24.h> / Подключаем библиотеку для работы с радиомодулем nRF24L01

const uint64_t pipe = 0xE8E8F0F0E1LL; // Указываем адрес конфигурации радиомодуля для обмена данными
RF24 radio(9,10);// vмодуль на пинах 9 и 10 // Инициализируем объект радиомодуля на пинах 2 и 9

byte pinLeftJoystickX = 14; // Указываем номер пина, на который подключен левый джойстик X
byte pinRightJoystickX = 15; // Указываем номер пина, на который подключен правый джойстик X
byte pinLeftJoystickSwitch = 4; // Указываем номер пина, на который подключена кнопка левого джойстика
byte pinRightJoystickSwitch = 3; // Указываем номер пина, на который подключена кнопка правого джойстика

boolean leftJoystickSwitch = 0; // Объявляем переменную для хранения состояния кнопки левого джойстика
boolean rightJoystickSwitch = 0; // Объявляем переменную для хранения состояния кнопки правого джойстика

boolean leftJoystickSwitchState; // Объявляем переменную для хранения текущего состояния кнопки левого джойстика
boolean rightJoystickSwitchState; // Объявляем переменную для хранения текущего состояния кнопки правого джойстика

int transmitData[4]; // Массив, хранящий передаваемые данные
int latestData[4]; // Массив, хранящий последние переданные данные
boolean flag = 0; // Флаг, указывающий на необходимость отправки данных по радио
boolean leftJoystickSwitchFlag = 0; // Флаг, указывающий на изменение состояния кнопки левого джойстика
boolean rightJoystickSwitchFlag = 0; // Флаг, указывающий на изменение состояния кнопки правого джойстика

unsigned long lastPressLeftJoystickSwitch; // Переменная, хранящая время последнего нажатия кнопки левого джойстика
unsigned long lastPressRightJoystickSwitch; // Переменная, хранящая время последнего нажатия кнопки правого джойстика

void setup() {
  pinMode(pinLeftJoystickSwitch, INPUT_PULLUP); // Устанавливаем пин, на который подключена кнопка левого джойстика, в режим входа с подтяжкой к питанию
  pinMode(pinRightJoystickSwitch, INPUT_PULLUP); // Устанавливаем пин, на который подключена кнопка правого джойстика, в режим входа с подтяжкой к питанию
 
  radio.begin(); // Активировать модуль

  radio.openWritingPipe(pipe);   // Мы - труба 0, открываем канал для передачи данных
  radio.setChannel(9);  // Выбираем канал (в котором нет шумов!)

  radio.setPALevel (RF24_PA_MAX); // Уровень мощности передатчика. На выбор RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
  radio.setDataRate (RF24_250KBPS); // Скорость обмена. На выбор RF24_2MBPS, RF24_1MBPS, RF24_250KBPS
  // Должна быть одинакова на приёмнике и передатчике!
  // При самой низкой скорости имеем самую высокую чувствительность и дальность!!

  radio.powerUp(); // Начать работу
  radio.stopListening(); // Не слушаем радиоэфир, мы передатчик
}

void loop() {
  leftJoystickSwitchState = !digitalRead(pinLeftJoystickSwitch); // Считать состояние переключателя левого джойстика
  rightJoystickSwitchState = !digitalRead(pinRightJoystickSwitch); // Считать состояние переключателя правого джойстика

  if(leftJoystickSwitchState == 1 && leftJoystickSwitchFlag == 0 && millis() - lastPressLeftJoystickSwitch > 50){ // Если нажат переключатель левого джойстика и флаг еще не поднят, и прошло более 50 миллисекунд после последнего нажатия
    leftJoystickSwitchFlag = 1;  // Поднять флаг переключения левого джойстика
    leftJoystickSwitch = !leftJoystickSwitch; // Поменять состояние переключателя левого джойстика
    lastPressLeftJoystickSwitch = millis(); / Запомнить время последнего нажатия переключателя левого джойстика
  }
  if(leftJoystickSwitchState == 0 && leftJoystickSwitchFlag == 1){ // Если переключатель левого джойстика отпущен и флаг поднят
    leftJoystickSwitchFlag = 0; // Опустить флаг переключения левого джойстика
  }
  if(rightJoystickSwitchState == 1 && rightJoystickSwitchFlag == 0 && millis() - lastPressRightJoystickSwitch > 50){ // Если нажат переключатель правого джойстика и флаг еще не поднят, и прошло более 50 миллисекунд после последнего нажатия
    rightJoystickSwitchFlag = 1; // Поднять флаг переключения правого джойстика
    rightJoystickSwitch = !rightJoystickSwitch; // Поменять состояние переключателя правого джойстика
    lastPressRightJoystickSwitch = millis(); // Запомнить время последнего нажатия переключателя правого джойстика
  }
  if(rightJoystickSwitchState == 0 && rightJoystickSwitchFlag == 1){ // Если переключатель правого джойстика отпущен и флаг поднят
    rightJoystickSwitchFlag = 0; // Опустить флаг переключения правого джойстика
  }
  
  transmitData[0] = analogRead(pinLeftJoystickX); // Записать данные с левого джойстика в массив для передачи
  transmitData[1] = analogRead(pinRightJoystickX); // Записать данные с правого джойстика в массив для передачи
  transmitData[2] = leftJoystickSwitch; // Записать состояние переключателя левого джойстика в массив для передачи
  transmitData[3] = rightJoystickSwitch;  // Записать состояние переключателя правого джойстика

  for (int i = 0; i < 4; i++) { // В цикле от 0 до числа каналов
    if (transmitData[i] != latestData[i]) { // Если есть изменения в transmit_data
      flag = 1; // Поднять флаг отправки по радио
      latestData[i] = transmitData[i]; // Запомнить последнее изменение
    }
  }

  if (flag == 1) {
    radio.powerUp(); // Включить передатчик
    radio.write(&transmitData, sizeof(transmitData)); // Отправить по радио
    flag = 0; // Опустить флаг
    radio.powerDown(); // Выключить передатчик
  }
}
First tests

First tests

In conclusion, the tests showed that the boat develops a speed of 1 m / s with a load in calm weather. Radio control works up to 700m in open area. Motor drivers overheat strongly, they need to be replaced with more powerful ones. Periodic deadwoods leak when reversing. It is better to buy ready-made solutions. There are not enough revolutions in the engines, more resourceful ones are needed. Plus, you need to refactor the entire code.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *