Подводная лодка на радиоуправлении из шприцов и Iskra Nano Pro

Тема в разделе "Глядите, что я сделал", создана пользователем issaom, 30 окт 2019.

  1. issaom

    issaom Гуру

    "я его слепила из того что было....."


    монтажная плата:
    монтажная плата.jpg
    Плата с управляющей электроникой:
    мп1.JPG
    И с установленными модулями
    мп2.JPG
    Вид с боку
    общий вид пл.jpg

    RC submarine - копия - копия.jpg
    На борту установлен GPS так что может работать как часы ;-)
    Будут вопросы - спрашивайте.....
     
  2. NikitOS

    NikitOS Public enemy Администратор

    А где самое интересное и сложное? :)
     
  3. issaom

    issaom Гуру

    Что именно ;-)
     
  4. NikitOS

    NikitOS Public enemy Администратор

    Код :)
     
  5. issaom

    issaom Гуру

    У меня есть только отладачная версия на написание которой у меня было около часа - такое стыдно выкладывать но раз хотишь.....
    Подлодка:
    Код (C++):
    //библиотека для работы с интерфейсом I2C
    #include <Wire.h>
    //библиотека для работы с датчиком напряжения
    #include <Adafruit_INA219.h>
    //создаем экземпляр для работы с датчиком
    Adafruit_INA219 ina219;
    /*----------------------------------------------------*/

    #define LL1 8
    #define RL1 7
    #define LL2 6 //~
    #define RL2 5 //~
    #define LL4 4
    #define LL3 3 //~
    #define RL3 2 //~
    #define RL4 21
    #define LED 20
    #define LMrev 16
    #define LMpwm 9  //~
    #define RMrev 17
    #define RMpwm 10 //~

    // Выбор аппапатного порта UART (расскомментируйте один из этих вариантов)
    #define Serial_0

    byte transm_arr[27];  // Массив который приходит с передатчика
    byte receiv_arr[27];  // Массив который отправляется передатчику

    // Подключаем библиотеку ПРИЕМНИКА
    #include <Serial27bReceiver.h>

    long previousMillis = 0;
    long interval = 0;

    void setup() {
      //  скорость соединения, максимальное время ожидания данных от передатчика(мс)
      startReceiver(9600, 400);
      // инициализация датчика напряжения
      ina219.begin();
      pinMode(LED, OUTPUT); // KEY BC547B LED-LIGHT
      pinMode(LL1, OUTPUT);  // LL1 помпа BLesft
      pinMode(RL1, OUTPUT);  // RL1 помпа RRight
      pinMode(LL2, OUTPUT);  // LL2 ~ airPump корма
      pinMode(RL2, OUTPUT);  // RL2 ~ airPump нос
      pinMode(LL4, OUTPUT);  // LL4 помпа FLesft
      pinMode(LL3, OUTPUT);  // LL3 ~ клапан корма
      pinMode(RL3, OUTPUT);  // RL3 ~ клапан нос
      pinMode(RL4, OUTPUT);  // RL4 помпа FRight
      pinMode(LMrev, OUTPUT);  // левый мотор реверс
      pinMode(LMpwm, OUTPUT);  // левый мотор скорость
      pinMode(RMrev, OUTPUT);  // правый мотор реверс
      pinMode(RMpwm, OUTPUT);  // правый мотор скорость
    }

    void loop() {

      //управление освещением
      digitalWrite(LED, bitRead(transm_arr[0], 0));
      //управление аквапомпами
      digitalWrite(LL1, bitRead(transm_arr[0], 4));
      digitalWrite(RL1, bitRead(transm_arr[0], 5));
      digitalWrite(LL4, bitRead(transm_arr[0], 6));
      digitalWrite(RL4, bitRead(transm_arr[0], 7));
      //управление клапанами и компрессорами погружения
      analogWrite(RL3, transm_arr[1]); // RL3 ~ клапан нос
      analogWrite(LL3, transm_arr[2]); // LL3 ~ клапан корма
      analogWrite(RL2, transm_arr[3]); // RL2 ~ airPump нос
      analogWrite(LL2, transm_arr[4]); // LL2 ~ airPump корма
      // управление моторами, а куда их крутить знает старший бит
      digitalWrite(LMrev, bitRead(transm_arr[6], 7));   // левый мотор туда сюда
      digitalWrite(RMrev, bitRead(transm_arr[7], 7));   // правый мотор туда сюда
      uint8_t SpeedLM = transm_arr[6];
      uint8_t SpeedRM = transm_arr[7];
      bitClear(SpeedLM, 7);
      bitClear(SpeedRM, 7);
      analogWrite(LMpwm, map(SpeedLM, 0, 127, 0, 255));
      analogWrite(RMpwm, map(SpeedRM, 0, 127, 0, 255));
      // отадем телеметрию !!!
      // датчик напряжения INA219
      float busvoltage = 0;
      //функция считывает напряжение между GND и V-
      busvoltage = ina219.getBusVoltage_V() * 10;
      busvoltage = round(busvoltage);
      // отдаем напряжение на передатчик
      receiv_arr[0] = busvoltage;
      if (digitalRead(13) == LOW) {
        if (rErrTime > 1) {
        transm_arr[0] = 1;
        transm_arr[1] = 0;
        transm_arr[2] = 0;
        transm_arr[3] = 0;
        transm_arr[4] = 0;
        transm_arr[6] = 0;
        transm_arr[7] = 0;
        }
      } else rErrTime =0;
      // autodiving
      if (bitRead(transm_arr[0],1)) {
      interval = transm_arr[5] * 1000;
      analogWrite(RL3, 255); // RL3 ~ клапан нос
      analogWrite(LL3, 255); // LL3 ~ клапан корма
      analogWrite(RL2, 255); // RL2 ~ airPump нос
      analogWrite(LL2, 255); // LL2 ~ airPump корма
      autodiving_label:
      unsigned long currentMillis = millis();
      //проверяем не прошел ли нужный интервал, если прошел то
      if(currentMillis - previousMillis > interval) {
        // сохраняем время последнего переключения
        previousMillis = currentMillis;      
      }  else {
      //проверяем не отменили ли команду с пульта
      if (bitRead(transm_arr[0],2)) interval=0;
      goto autodiving_label;
      }
      }
      delay(100);
    }
     
    sendsay, ИгорьК, Daniil и ещё 1-му нравится это.
  6. issaom

    issaom Гуру

    Передатчик: (отладочная плата)
    Код (C++):
    //библиотека для работы с интерфейсом I2C
    #include <Wire.h>
    //библиотека для работы с датчиком напряжения
    #include <Adafruit_INA219.h>
    //создаем экземпляр для работы с датчиком
    Adafruit_INA219 ina219;

    #include <LiquidCrystal.h>
    LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

    // Выбор аппапатного порта UART
    #define Serial_2

    byte transm_arr[27];  // Массив который отправляется на приемник
    byte receiv_arr[27];  // Массив который приходит с приемника

    // Подключаем библиотеку ПЕРЕДАТЧИКА
    #include <Serial27bTransmitter.h>

    boolean backL = 0;  //левый сзаду enable fix
    boolean backR = 0;  //правый сзаду enable fix
    boolean frontL = 0; //левый спереди enable fix
    boolean frontR = 0; //правый спереди enable fix

    boolean RL3 = 0; // RL3 ~ клапан нос
    boolean LL3 = 0; // LL3 ~ клапан корма
    boolean RL2 = 0; // RL2 ~ airPump нос
    boolean LL2 = 0; // LL2 ~ airPump корма

    boolean RL3y = 0; // RL3 ~ клапан нос
    boolean LL3y = 0; // LL3 ~ клапан корма
    boolean RL2y = 0; // RL2 ~ airPump нос
    boolean LL2y = 0; // LL2 ~ airPump корма

    boolean key4 = 0; // KEY BC547B LED-LIGHT

    boolean fixEnaB = 0; // кнопка управляющая фиксацией моторов
    boolean fixEnaS = 0; // фиксировать кнопки или нет

    void setup() {
      // скорость соединения, интервал посылок приемнику(мс)
      startTransmitter(9600, 200);

      pinMode(42, INPUT_PULLUP); // кнопка освещение
      pinMode(36, INPUT_PULLUP); // №1 ULN2803 (1-2) enable
      pinMode(37, INPUT_PULLUP); // №1 ULN2803 (3-4) enable
      pinMode(38, INPUT_PULLUP); // №1 ULN2803 (5-6) enable
      pinMode(39, INPUT_PULLUP); // №1 ULN2803 (7-8) enable

      pinMode(40, INPUT_PULLUP); // autodiving
      pinMode(25, INPUT_PULLUP); // autodiving cancel

      pinMode(29, INPUT_PULLUP); // QE4 №2 ULN2803 (1-2) //левый спереди
      pinMode(30, INPUT_PULLUP); // QG6 №2 ULN2803 (5-6) //левый сзади
      pinMode(22, INPUT_PULLUP); // QF5 №2 ULN2803 (3-4) //правый спереди
      pinMode(23, INPUT_PULLUP); // QH7 №2 ULN2803 (7-8) //правый сзади

      pinMode(41, INPUT_PULLUP); // enable Fix Motor button

      lcd.begin(20, 4);

      // инициализация датчика напряжения
      ina219.begin();

      // отключаем индикацию RX/TX на пульте
      pinMode(14, OUTPUT);   digitalWrite(14, 1);
      pinMode(15, OUTPUT);   digitalWrite(15, 1);
      pinMode(18, OUTPUT);   digitalWrite(18, 1);
      pinMode(19, OUTPUT);   digitalWrite(19, 1);
    }

    void loop() {

      // Фиксировать кнопки двигателей или нет
      if (digitalRead(41))   fixEnaB = false;
      if (!digitalRead(41) && !fixEnaB) {
        fixEnaS = !fixEnaS;
        fixEnaB = true;
      }

      // Управление освещением fix
      if (digitalRead(42))   key4 = false;
      if (!digitalRead(42) && !key4) {
        bitWrite(transm_arr[0], 0, !bitRead(transm_arr[0], 0));
        key4 = true;
      }
      // bitWrite(transm_arr[0], 0, !digitalRead(42)); no fix

      // autodiving
      if (!digitalRead(40)) {
      bitWrite(transm_arr[0], 1, !digitalRead(40)); // no fix
      transm_arr[5] = map(analogRead(A12), 0, 1023, 0, 255);
      } else {
      bitWrite(transm_arr[0], 1, 0);
      transm_arr[5] = 0;
      }
      // autodiving cancel
      bitWrite(transm_arr[0], 2, !digitalRead(25)); // no fix
     
      if (digitalRead(36))   RL3 = false;
      if (!digitalRead(36) && !RL3) {
        RL3y = !RL3y;
        RL3 = true;
      }
      //bitWrite(transm_arr[0], 4, !digitalRead(36)); nofix

      if (digitalRead(37))   LL3 = false;
      if (!digitalRead(37) && !LL3) {
        LL3y = !LL3y;
        LL3 = true;
      }
      //bitWrite(transm_arr[0], 5, !digitalRead(37)); nofix

      if (digitalRead(38))   RL2 = false;
      if (!digitalRead(38) && !RL2) {
        RL2y = !RL2y;
        RL2 = true;
      }
      // bitWrite(transm_arr[0], 6, !digitalRead(38)); nofix

      if (digitalRead(39))   LL2 = false;
      if (!digitalRead(39) && !LL2) {
        LL2y = !LL2y;
        LL2 = true;
      }

      // если кнопки нажаты заталкиваем значения для клапанов и компрессоров
      if (RL3y) transm_arr[1] = map(analogRead(A8), 0, 1023, 0, 255); else  transm_arr[1] = 0; // RL3 ~ клапан нос
      if (LL3y) transm_arr[2] = map(analogRead(A9), 0, 1023, 0, 255); else  transm_arr[2] = 0; // LL3 ~ клапан корма
      if (RL2y) transm_arr[3] = map(analogRead(A10), 0, 1023, 0, 255); else transm_arr[3] = 0; // RL2 ~ airPump нос
      if (LL2y) transm_arr[4] = map(analogRead(A11), 0, 1023, 0, 255); else transm_arr[4] = 0; // LL2 ~ airPump корма

      // Управление L293D и выходами без pwm
      uint16_t leftRES = analogRead(A14);
      uint16_t righRES = analogRead(A15);

      // левый двигатель EN2
      uint8_t pwmL;

      if (leftRES >= 563) {
        // вперед
        pwmL = map(analogRead(A14), 563, 1023, 0, 127);
        bitWrite(pwmL,7,1);
      } else if (leftRES <= 460) {
        // назад
        pwmL = map(analogRead(A14), 460, 0, 0, 127);
      } else {
        // стоп
        pwmL = 0;
      }

      // правый двигатель EN1
      uint8_t pwmR;
      if (righRES >= 563) {
        // вперед
        pwmR = map(analogRead(A15), 563, 1023, 0, 127);
        bitWrite(pwmR,7,1);  
      } else if (righRES <= 460) {
        // назад
        pwmR = map(analogRead(A15), 460, 0, 0, 127);
      } else {
        // стоп
        pwmR = 0;
      }
      // запихиваем инфу для моторчегоф
      transm_arr[6] = pwmL;
      transm_arr[7] = pwmR;

      //запихиваем инфу для кнопок без PWM фиксируем или нет по желанию
      if (fixEnaS) {

        //левый сзаду
        if (digitalRead(30))   backL = false;
        if (!digitalRead(30) && !backL) {
          bitWrite(transm_arr[0], 4, !bitRead(transm_arr[0], 4));
          backL = true;
        }

        //правый сзаду
        if (digitalRead(23))   backR = false;
        if (!digitalRead(23) && !backR) {
          bitWrite(transm_arr[0], 5, !bitRead(transm_arr[0], 5));
          backR = true;
        }

        //левый спереди
        if (digitalRead(29))   frontL = false;
        if (!digitalRead(29) && !frontL) {
          bitWrite(transm_arr[0], 6, !bitRead(transm_arr[0], 6));
          frontL = true;
        }

        //правый спереди
        if (digitalRead(22))   frontR = false;
        if (!digitalRead(22) && !frontR) {
          bitWrite(transm_arr[0], 7, !bitRead(transm_arr[0], 7));
          frontR = true;
        }
      } else {
        bitWrite(transm_arr[0], 4, !digitalRead(30));
        bitWrite(transm_arr[0], 5, !digitalRead(23));
        bitWrite(transm_arr[0], 6, !digitalRead(29));
        bitWrite(transm_arr[0], 7, !digitalRead(22));
      }


      //выводим информацию на дисплей
      lcd.clear();

      lcd.setCursor(0, 0);
      lcd.print(transm_arr[1]);
      lcd.setCursor(0, 1);
      lcd.print(transm_arr[2]);
      lcd.setCursor(0, 2);
      lcd.print(transm_arr[3]);
      lcd.setCursor(0, 3);
      lcd.print(transm_arr[4]);

      lcd.setCursor(4, 0);
      lcd.print('-');
      lcd.print(pwmL);

      lcd.setCursor(4, 1);
      lcd.print('-');
      lcd.print(pwmR);

      lcd.setCursor(4, 2);
      lcd.print("d-");
      lcd.print(bitRead(transm_arr[0], 1));

      lcd.setCursor(4, 3);
      lcd.print(map(analogRead(A12), 0, 1023, 0, 255));

      // управление c pwm
      lcd.setCursor(9, 0);
      lcd.print(RL3y);
      lcd.setCursor(9, 1);
      lcd.print(LL3y);
      lcd.setCursor(9, 2);
      lcd.print(RL2y);
      lcd.setCursor(9, 3);
      lcd.print(LL2y);

      // управление без pwm
      lcd.setCursor(18, 0);
      lcd.print(bitRead(transm_arr[0], 6));
      lcd.print(bitRead(transm_arr[0], 7));
      lcd.setCursor(18, 1);
      lcd.print(bitRead(transm_arr[0], 4));
      lcd.print(bitRead(transm_arr[0], 5));

      //напряжение на приемнике
      lcd.setCursor(15, 2);
      lcd.print("rV");
      float ShowV = receiv_arr[0];
      ShowV = ShowV / 10;
      lcd.print(ShowV, 1);

      // напряжение на передатчике
      float busvoltage = 0;
      //функция считывает напряжение между GND и V-
      busvoltage = ina219.getBusVoltage_V();
      lcd.setCursor(15, 3);
      lcd.print("tV");
      lcd.print(busvoltage, 1);

      // фиксация кнопок и освещение
      lcd.setCursor(15, 0);
      lcd.print(fixEnaS);
      lcd.print(bitRead(transm_arr[0], 0));

      delay(100);
    }
    Схема пульта:
    Пультик.jpg
     
    ИгорьК, Daniil и NikitOS нравится это.
  7. issaom

    issaom Гуру

    В принципе можно вот тут все посмотреть :)
    Протокол.JPG
     
    ИгорьК, Daniil и NikitOS нравится это.
  8. Daniil

    Daniil Гуру

    Очень круто!
    А как, если нужно, будете решать проблему со связью при погружении на большую глубину (большие потери для сигнала)?
     
  9. parovoZZ

    parovoZZ Гуру

    выкидывать буй на поверхность.
     
  10. issaom

    issaom Гуру

    До этого еще долго - для начала необходимо выяснить на какую максимальную глубину можно погружаться. На глубине давление будет давить на штоки поршней и не даст им выехать - правда в этой конструкции есть одна фишка: можно закрыть клапаны, закачать небольшой объем воздуха и только потом вставить поршни. Тогда они будут находится изначально под избыточным давлением, что поможет всплывать с большей глубины. Связь под водой не думаю что сильно необходима - я планировал написать несколько программ для автономного управления моделью под водой. (типа той которая уже реализована - погрузился - постоял на дне - всплыл). Вообще на многих форумах писали что связь под водой не возможна в принципе - поэтому любопытно какую толщу воды сможет пробить HC-12. Сильно далеко не отплывал и сильно глубоко я не погружался - потери отдельных пакетов под водой были - но это не критично так я долблю посылками 5 раз в секунду и что-то из этого все равно долетало - на удобстве управления это вообще ни как не сказывалось. На самой субмарине обработчик потери связи был настроен на потерю 2-х пакетов подряд.....
    Код (C++):
    if (rErrTime > 1) {
    Ета вот строчка кода - и он почти не срабатывал )))
     
  11. Интересный факт...
    В прошлом занимался определением координат водолазов и получением телеметрии (температура тела, кардиометрия и спирометрия).
    Для связи под водой, которая как оказалась не возможна в принципе, использовали ультразвуковые пьезокерамические преобразователи.
    Ультразвук прекрасно распространяется под водой.
     
    issaom нравится это.
  12. issaom

    issaom Гуру

    А у Вас нет идей как можно датчик погружения сделать на маленькие глубины? (1....3м)
     
  13. В 1985-88гг глубины были больше :) тензодатчики тоже, что сейчас на 1..3м можно использовать не знаю
     
  14. parovoZZ

    parovoZZ Гуру

    никакую. Вода - проводник со всем вытекающими. Связь возможна только с помощью магнитных полей на низкой частоте.
     
  15. issaom

    issaom Гуру

    Ты видео смотрел? Все прекрасно работает даже когда антенна на 0,5м уходит под воду
     
  16. b707

    b707 Гуру

    а как оно двигается? моторов не видно. И на схеме нет
     
  17. issaom

    issaom Гуру

    Аквапомпы - водометный движитель типа :D - костыль чтобы не делать дейдвуд который в домашних условиях сделать сложно - в видео все показано )))
     
  18. eevgeniy1957

    eevgeniy1957 Нерд

    Классно! А видео в реальном времени можно получать на смартфон?
     
  19. issaom

    issaom Гуру

    Сегодня проводил тесты - ближе к вечеру выложу результаты
     
    eevgeniy1957 нравится это.
  20. issaom

    issaom Гуру

    1.5 метра воды HC-12 шьет на ура - можно толкать датчики в колодцы :D (ZZ проехал куда-то мимо). Тест в бассейне + видео с камеры SQ23.
    Под водой в реал-тайм видео не получить Wi Fi там разумеется не работает - но можно всплыть и забрать файл на смартфон и посмотреть его не подплывая к берегу :) - как то так....

     
    zveros, eevgeniy1957, Daniil и 2 другим нравится это.