logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Drugie życie modeli RC, czyli zdalne sterowanie by Frog_Qmak

Frog_Qmak 30 Lis 2023 16:31 5796 17
  • Zdalnie sterowany samochód terenowy z anteną, stojący na drewnianej podłodze.

    Witajcie

    Chciałbym zaprezentować i podzielić się uniwersalną platformą do obsługi zdalnie sterowanych modeli.
    Wiele lat nie publikowałem niczego na forum - nie znaczy to, że nic się nie działo, natomiast nie zawsze nadawało się to do pokazania ;)


    Wszystko zaczęło się od tego, że chciałem zarówno zrobić coś, co dałoby mi frajdę, jak i było inspiracją do "grzebania" w kablach i Arduino. Zamówiłem więc sobie z chińskiego sklepu internetowego (sinoning.com) podwozie czołgu i tak zaczęła się przygoda... po czołgu pojawił się mini-samochodzik (również z tego samego sklepu), a następnie, kupione za 1/3 wartości nowych pojazdów pełnoprawny samochodzik z napędem 4x4 i zdalnie sterowana szybka łódka, kupione na Allegro jako "uszkodzone". Uszkodzenie samochodzika polegało na dostaniu się do środka wody i zatarciu silnika (samochodzik teoretycznie jest wodoodporny i ma dodatnią pływalność i może również "poruszać się" po wodzie), który udało się (siłą, a następnie kręcąc ręcznie) ożywić, natomiast łódka teoretycznie miała uszkodzone sterowanie, natomiast nie sprawdzałem, co się stało, gdyż od samego początku chciałem zainstalować własne...

    Kilka słów o samym sterowaniu:
    Pilot składa się z 2 joysticków (moduły Arduino), Arduino Nano, akumulatora Li-Ion, układu BMS, przetwornicy step-up 5V i radia (moduł HC-12). Żadne rocket science. Ogromny problem sprawiło mi zdiagnozowanie problemu, polegającego na tym, że BMS nie chciał ładować akumulatora - dioda bardzo szybko migała, jak gdyby nie było gdzieś styku, lub moduł był wzbudzany. W akcie desperacji przelutowałem go bezpośrednio do akumulatora (wcześniej przez wyłącznik) i to rozwiązało problem. Nie wiem, dlaczego wyłącznik powodował takie problemy - być może styki dawały jakiś opór elektryczny i moduł był właśnie wzbudzany np. działaniem radia, lub przetwornicy. Pilot posiada dwa tryby (tryb zmienia się długim przytrzymaniem lewego joystkicka): tryb autka/łódki (domyślny - lewy joystick przód/tył, prawy joystick prawo/lewo), a także tryb czołgu (oba joystkicki przód/tył do sterowania różnicowego). Krótsze przytrzymanie lewego joysticka zmienia tryb świecenia lamp (jasny/średni/wyłączony) w dużym samochodziku.

    Odbiorniki składają się również z Arduino Nano, HC-12, a także (samochodziki, czołg) BMS-a i mostka H do sterowania silnikami (w łódce silnikiem, ster obsługiwany jest serwomechanizmem). Najmniejszy samochodzik zasilany jest jednym ogniwem LI-ION i przetwornicą STEP-UP, stąd duży kondensator, by spadki napięcia nie resetowały Arduino. Pozostałe modele działają na dwóch 18650 (duży samochodzik również posiada kondensator na zasilaniu, ale nie jest on potrzebny).

    Dodatkowe informacje
    Duży samochód posiada światła na bazie WS2812B (dwa białe z przodu, jedno czerwone z tyłu).

    Sterowanie łódką jest bardziej skomplikowane, ponieważ do obsługi serwa nie można wykorzystać "zwykłego kodu" PWM, gdyż odbywa się ono w inny sposób. Wszystkie odbiorniki mają identyczny kod; Arduino przy starcie sprawdza, czy na jednym z pinów jest odpowiednie napięcie (podane z wyjścia 3,3V) i jeżeli tak, to aktywuje "tryb łódki". Pozostałe modele nie mają zmostkowanego tego pinu - w ten sposób program jest uniwersalny a i nie trzeba dodawać kolejnego "trybu" w pilocie.

    Łódka posiada zabezpieczenie przed niskim napięciem baterii - jest ono mierzone przez dzielnik napięcia i posiada dwa progi aktywacji. Przy osiągnięciu pierwszego, emitowany jest przez kilka sekund sygnał dźwiękowy (5V buzzer sterowany przez tranzystor BS170). Po osiągnięciu drugiego, sygnał dźwiękowy włącza się na stałe, a moc silnika zostaje obniżona (PWM max 50%). Zastosowany w fabrycznych ogniwach BMS nie odcina zasilania nawet przy głębokim rozładowaniu, co ma sens, bo lepiej jest zajechać akumulatory, niż utknąć łódką 10 metrów od brzegu...
    Serwo sterowane jest za pomocą software'owej biblioteki. Wynika to z tego, że domyślna biblioteka do obsługi serw miała konflikt z biblioteką odpowiedzialną za sterowanie WS2812 (prawdopodobnie konflikt z powodu korzystania z tego samego timera).

    Program posiada również okomentowane sekcje związane z testowaniem baterii w łódce - zanim odkryłem, że BMS jej nie odcina, napisałem skrypt, który zapisywał w EEPROM-ie najniższe zmierzone napięcie zasilania i po podłączeniu Arduino do komputera, wysyłał je przez Serial. Chciałem wiedzieć, do jakiego poziomu można rozładować ogniwa, by odpowiednio ustawić progi zadziałania zabezpieczeń, jednak okazało się to niepotrzebne.

    Pilot i duży samochodzik posiadają złącza do ładowania akumulatorów; do małego samochodzika i łódki są ładowarki.

    Ogólnie odkryłem, że "spiralna" antena w nadajniku działa kiepsko, natomiast sprawdza się zwykły drut o odpowiedniej długości. Anteny w pilocie i samochodzikach są montowane w gniazdach goldpin, by można je było łatwo zdemontować do transportu. Łódka posiada domyślną antenę spiralną. Radia HC-12 są bardzo fajne, teoretyczny zasięg (przy niskim bitrate, bezpośredniej widoczności) wynosi nawet kilometr. W załączniku dodaję dwa dokumenty, które przydadzą się do ich obsługi - zawierają opis funkcji i konfiguracji.

    Kalibracja jest potrzebna, by dostosować kod do danego joysticka (punkt neutralny) i serwa (również). Wartości są opisane w komentarzach.

    Schematów nie posiadam, jednak myślę, że na podstawie opisów i zdjęć można bez trudu załapać co i jak. Wiem, że (szczególnie w pilocie) jest pajęczyna kabli, ale szkoda mi czasu na projektowanie płytek, frajdę daje mi elektronika i pisanie programu.
    Kod jest mocno okomentowany (po angielsku), więc zrozumienie programów nie powinno sprawiać problemów. Zasięg w wybranej konfiguracji (bitrate radia, anteny) wynosi minimum 200m (być może więcej, nie sprawdzałem, przy takiej odległości trudno jest już sterować modelem).
    Jeżeli ktoś jest zainteresowany, warto śledzić ten temat, jeżeli pojawiłaby się aktualizacja programu(ów), będę dodawał.

    Poniżej zdjęcia i kod.
    Pilot z dwoma joystickami na czarnym pudełku do zdalnego sterowania.
    Wnętrze pilota z zestawem elektroniki i podłączonymi przewodami.
    Podwozie zdalnie sterowanego modelu samochodu z elektroniką Arduino i kablami.
    Zbliżenie na wnętrze obudowy elektronicznego projektu z komponentami.
    Zdalnie sterowany samochód terenowy z anteną, stojący na drewnianej podłodze.
    Układ elektroniczny z modułami Arduino w wnętrzu zdalnie sterowanego pojazdu.
    Zbliżenie na podwójne ogniwo akumulatora Li-Ion zamontowane w czarnym etui, otoczone przewodami i bezpiecznikiem.
    Zdalnie sterowana łódka z widocznym wnętrzem i komponentami elektronicznymi.
    Zbliżenie na elektronikę wewnątrz pomarańczowej zdalnie sterowanej łodzi.

    NADAJNIK
    // delay(1);//It is necessary for proper message sending. Without this messages sometimes are merged.
    //$$ separates values, ! ends the message
    
    
    #include <SoftwareSerial.h>
    SoftwareSerial mySerial(2, 3); //RX, TX
    
    //L: 3-507-1017 --> 1020 for smooth operation [506;508]
    //R: 3-526-1017 --> 1020 for smooth operation [525;527]
    //STEER [R]: 0-517-1022 [521;523]
    int ax; //analog read joystick A
    byte af; //analog joystick A forward
    byte ab; //analog joystick A backward
    
    int bx; //analog read joystick B
    byte bf; //analog joystick B forward
    byte bb; //analog joystick B backward
    
    boolean mode = false; //Tank or Car mode (default: car)
    int buttontest; //conunting time of button being pressed
    
    String toSend;
    
    
    void setup()
    {
      pinMode(4, OUTPUT);
      pinMode(12, OUTPUT);
      pinMode(11, OUTPUT);
      pinMode(10, OUTPUT);
      pinMode(9, OUTPUT);
      pinMode(5, OUTPUT);
      pinMode(6, OUTPUT);
    
      digitalWrite(4, HIGH); //DISABLE AT MODE
      digitalWrite(12, HIGH);//RIGHT JOYSTICK VCC
      digitalWrite(11, LOW);//RIGHT JOYSTICK GND
      digitalWrite(10, HIGH);//LEFT JOYSTICK VCC
      digitalWrite(9, LOW);//LEFT JOYSTICK GND
      digitalWrite(5, LOW);//BUZZER GND
      digitalWrite(6, LOW);//BUZZER VCC
    
      pinMode(A0, INPUT); //LEFT JOYSTICK BUTTON
      pinMode(A1, INPUT); //RIGHT JOYSTICK BUTTON
      pinMode(A5, INPUT); //RIGHT JOYSTICK FORWARD & BACKWARD
      pinMode(A6, INPUT); //RIGHT JOYSTICK RIGHT & LEFT
      pinMode(A2, INPUT); //LEFT JOYSTICK FORWARD & BACKWARD
    
      Serial.begin(9600);
      pinMode(4, OUTPUT); //for MODE setup
      digitalWrite(4, LOW); //ENTER AT MODE
      delay(50);//necessary to wait after mode change
      mySerial.begin(9600);//9600 adjust after adjusting communication speed using AT+BXXX
      mySerial.println("AT+FU3");
      delay(50);
      mySerial.println("AT+B9600");//9600
      delay(50);
      mySerial.println("AT+RX"); //AT MODE REQUIRED TO BE ON (4 LOW)
      delay(100);
      digitalWrite(4, HIGH); //EXIT AT MODE
      delay(50);//necessary to wait after mode change
    }
    
    
    
    void loop()
    {
      //RESET OLD VALUES TO AVOID CONFLICTS
      af = 0;
      ab = 0;
      bf = 0;
      bb = 0;
    
    
    
      ////////////////////////////////STEERING MODE CHECK
      if (analogRead(A0) < 10) //BUTTON PRESSED
    
      {
        buttontest = buttontest + 1; //COUNTING TIME OF BUTTON BEING PRESSED
        mySerial.print("BUTTON PRESSED !");
        Serial.println("BUTTON PRESSED !");
      }
      else
      {
        buttontest = 0; //RESET IN CASE OF ACCIDENTAL CLICK
      }
    
      //Serial.println(buttontest);
    
      if (buttontest == 40 ) //BUTTON PRESSED - SENT LIGHT ADJUST SIGNAL
      {
        toSend = String("E") + "!"; 
        mySerial.print(toSend);
        delay(1);
        digitalWrite(6, HIGH);
        delay(100);
        digitalWrite(6, LOW);
        delay(100);
        digitalWrite(6, HIGH);
        delay(100);
        digitalWrite(6, LOW);
        delay(1000);
      }
    
    
      if (buttontest == 41) //BUTTON PRESSED - SENT LIGHT ON/OFF SIGNAL
      {
        toSend = toSend = String("F") + "!"; 
        mySerial.print(toSend);
        delay(1);
        digitalWrite(6, HIGH);
        delay(1000);
        digitalWrite(6, LOW);
      }
    
    
    
      if (buttontest >= 42) //BUTTON PRESSED - CHANGE TO TANK MODE
      {
        mode = !mode;
        buttontest = 0;
        mySerial.print("MODE CHANGED !");
        delay(1);
        Serial.println("MODE CHANGED !");
        digitalWrite(6, HIGH);
        delay(4000);
        digitalWrite(6, LOW);
      }
    
    
    
      ////////////////////////////////////////LEFT JOYSTICK READ & CALCULATE
      ax = analogRead(A2); //LEFT JOYSTICK
      //  if (mode == 1)
      //  {
      //    Serial.print("A2 LEFT:  ");
      //    Serial.println(ax);
      //  }
      //  else
      //  {
      //    Serial.print("A2 MOVE: ");
      //    Serial.println(ax);
      //  }
    
    
    
    //Serial.print("AX READ: ");
    //Serial.println(ax);
    
      //af is PWM for A engine forward
      if (ax > 508)
      {
        af = map(ax, 508, 1020, 0, 255); //af is PWM for A engine forward
        //Serial.print("LEFT FORWARD: ");
        //Serial.println(af);
        toSend = "A" + String(af, DEC) + "!"; 
        mySerial.print(toSend);
        delay(5);
      }
    
    if (ax >= 506 && ax <=508) //to send zero command in case of neutral joistick position
    {
         toSend = "A" + String(0, DEC) + "!";  
         mySerial.print(toSend);
         delay(5);
         toSend = "B" + String(0, DEC) + "!";  
         mySerial.print(toSend);
         delay(5);
    }
    
      if (ax < 506)
      {
        ab = map(ax, 3, 506, 255, 0); //ab is PWM for A engine backward
        //Serial.print("LEFT BACKWARD: ");
        //Serial.println(ab);
        toSend = "B" + String(ab, DEC) + "!"; 
        mySerial.print(toSend);
        delay(5);
      }
      ////////////////////////////////END OF LEFT JOYSTICK READ & CALCULATE
    
    
    
      ////////////////////////////////////////RIGHT JOYSTICK READ & CALCULATE
      ///////////READING POSITION FROM CORRECT JOYSTICK OUTPUT
      if (mode == 1)
      {
        bx = analogRead(A5); //IF IN TANK MODE
        //    Serial.print("A5 RIGHT: ");
        //   Serial.println(bx);
      }
      else
      {
        bx = analogRead(A4); //IF IN CAR MODE
        //Serial.print("A6 STEER: ");
        //Serial.println(bx);
      }
      ///////////END OF READING FROM CORRECT JOYSTICK OUTPUT
    
      ///////////CALCULATING PWM ACCORDING TO
      //JOYSTICK'S ZERO CALIBRATION POSITION
      if (mode == 1) //IF IN TANK MODE
      {
        if (bx > 527)
        {
          bf = map(bx, 527, 1020, 0, 255); // bf is PWM for B engine forward
          //Serial.print("RIGHT FORWARD: ");
          //Serial.println(bf);
          toSend = "C" + String(bf, DEC) + "!"; 
          mySerial.print(toSend);
          delay(5);
        }
    
        if (bx < 525)
        {
          bb = map(bx, 3, 527, 255, 0); //bb is PWM for B engine backward
          //Serial.print("RIGHT BACKWARD: ");
          //Serial.println(bb);
          toSend = "D" + String(bb, DEC) + "!"; 
          mySerial.print(toSend);
          delay(5);
        }
      }
      else //IF IN CAR MODE
      {
        if (bx > 517)
        {
          bf = map(bx, 517, 1022, 0, 255); //bf is PWM for B engine forward
          //Serial.print("STEER RIGHT: ");
          //Serial.println(bf);
          toSend = "C" + String(bf, DEC) + "!"; 
          mySerial.print(toSend);
          //Serial.println(toSend);
          delay(5);
        }
    
    if (bx >= 516 && bx <=517) //to send zero command in case of neutral joistick position
    {
         toSend = "C" + String(0, DEC) + "!";  
         mySerial.print(toSend);
         delay(5);
         toSend = "D" + String(0, DEC) + "!";  
         mySerial.print(toSend);
         delay(5);
    }
    
        if (bx < 516)
        {
          bb = map(bx, 0, 517, 255, 0); //bb is PWM for B engine backward
          //Serial.print("STEER LEFT: ");
          //Serial.println(bb);
          toSend = "D" + String(bb, DEC) + "!"; 
          mySerial.print(toSend);
          //Serial.println(toSend);
          delay(5);
        }
      }
    
      ////////////////RIGHT JOYSTICK DIAG
      //Serial.print("AX: ");
      //Serial.println(ax);
      //Serial.print("BX: ");
      //Serial.println(bx);
      /////////////////////////////////END OF LEFT JOYSTICK READ & CALCULATE
    
    
    }// END OF LOOP


    ODBIORNIK
    #include <SoftwareSerial.h>
    #include <Adafruit_NeoPixel.h>
    //#include <EEPROM.h>
    #include <PWMServo.h>//software PWM used because servo library had conflict with other used functions using PWM generated by the same internal closk
    PWMServo myservo;  // create servo object to control a servo
    SoftwareSerial mySerial(2, 3); //RX, TX
    Adafruit_NeoPixel
    strip = Adafruit_NeoPixel(3, 13, NEO_GRB + NEO_KHZ800); //NO of pixels, PIN
    byte af, ab, bf, bb;
    boolean light = 0; //LIGHT ON/OFF, default = OFF
    boolean change = false; // flag to mark change to use WS2812B communication once for light ON/OFF. Constant communication interrupts message receiving
    boolean lightadjustment = false; // flag to mark change to use WS2812B communication once for light level adjustment. Constant communication interrupts message receiving
    byte xy; //light brightness pre set setting
    byte brightness = 64; //brightness level (0-255), default = 64
    boolean boatmode;// if true, then boat mode
    byte modecount = 0; //no. of successful read sequences
    int  bootval;// voltage check at boot to detect whether it is a boat or not
    byte neutral = 85; //neutral position of servo
    byte servoangle; //servo's angle
    int serwoPWM;// calculated value to steer servo by generating PWM
    unsigned long counter; //time from last message
    unsigned long nowtime; //time now. Used further to calculate time past since previous loop
    //unsigned long pinginterval; // for signalling range ping messages
    float voltageread; //voltage read at input
    float batvoltage; //calculated battery level (incl. calculated offset)
    byte mediumdischargecount = 0; //to avoid one - time battery voltage drops,alarm will be activated after few times threshold is reached
    byte deepdischargecount = 0; //to avoid one - time battery voltage drops,alarm will be activated after few times threshold is reached
    long mediumbatlevelalarmtime; //time of buzzer's activation
    boolean mediumbatlevelalarmactivation = false;
    boolean deepdischargealarmactivation = false;
    
    //int lowestbatvoltage; ////////////////////USED ONLY AT BOOT TO HAVE ANY VALUE FOR IF FUNCTION TO COMPARE TO
    //int lowestbatEEPROM;
    
    
    
    void setup()
    {
      //writeIntIntoEEPROM(7000);//EEPROM LOW RESET USED TO START A NEW RECORDING
      //PIN D11 BURNT !!!!!!!!!!!!
      //PINS A6&A7 cannot be used as analog output in Arduino Nano
      pinMode(4, OUTPUT); //AT MODE SWITCH
      pinMode(5, OUTPUT);
      pinMode(6, OUTPUT);
      pinMode(10, OUTPUT);//SERVO
      pinMode(13, OUTPUT); //built in led + WS2812B
      pinMode(A1, OUTPUT); //GND for voltage sensing
      pinMode(A0, INPUT); //voltage check at boot to detect whether it is a boat or not
      pinMode(A2, INPUT); //battery voltage check
      pinMode(A5, OUTPUT); //low battery buzzer output
      digitalWrite(A1, LOW); //GND for voltage sensing
      digitalWrite(A5, LOW); //deactivate buzzer
      digitalWrite(4, LOW); //ENTER AT MODE
      delay(50);//necessary to wait after AT mode change
    
      Serial.begin(9600);
      mySerial.begin(9600);//adjust after adjusting communication speed using AT+BXXX
      mySerial.println("AT+FU3");
      delay(50);
      mySerial.println("AT+B9600");
      delay(50);
      mySerial.println("AT+RX"); //AT MODE REQUIRED TO BE ON (4 LOW)
      delay(100);
      digitalWrite(4, HIGH); //EXIT AT MODE
      delay(50);//necessary to wait after mode change\
      analogReference(INTERNAL); //1,1V
      modecheck(); //Function to detect whether hardware is a boat or not
    
      analogReference(INTERNAL); //1,1V for battery measurement
    
      if (boatmode == true)
      {
        myservo.attach(SERVO_PIN_B); //Library works only at PIN9 and 10 (already used)
        servostart(); //Sequence for servo movement at boat's boot
        testbeep(); //Test buzzer
        //Serial.print("LOWEST VOLTAGE FROM EEPROM: ");
        //Serial.println(readIntFromEEPROM());
      }
      else
      {
        strip.begin(); // INITIALIZE NeoPixel strip object
        strip.clear();
        strip.show();
        startsequence(); //LED BLINK at car's boot
      }
    } ///////////////////////////////////////////END OF SETUP
    
    
    void loop()
    {
      ///////////////////////LOST CONNECTION PROTECTION
      nowtime = millis();
      if ((nowtime - counter) > 1000) //1000 MS SIGNAL LOST TRIGGER
      {
        //Serial.println(nowtime-counter);
        //Serial.println("CONNECTION LOST !!!");
        if (boatmode == false) //IF IN CAR MODE
        {
          digitalWrite(11, LOW);
          digitalWrite(10, LOW);
          digitalWrite(6, LOW);
          digitalWrite(5, LOW);
        }
        else //IF IN BOAT MODE
        {
          digitalWrite(6, LOW);
          digitalWrite(5, LOW);
        }
      }
      ///////////////////////END OF LOST CONNECTION PROTECTION
    
      //////////////////////////100 MS SIGNAL PING TRIGGER
      //    if ((nowtime - pinginterval) > 50)
      //    {
      //      //Serial.println("PING INTERVAL");
      //      //Serial.println(nowtime-pinginterval);//Loop time
      //      pinginterval = millis();
      //      ();
      //    }
      ///////////////////////END OF SIGNAL PING TRIGGER
    
    
      ////////////////////////////////////// MESSAGE RECEIVING
      while (mySerial.available())
      {
        static String receivedMessage = ""; // Store the received message
        char receivedChar = mySerial.read();
    
        if (receivedMessage.length() == 0)
        {
          if (receivedChar == 'A' || receivedChar == 'B' || receivedChar == 'C' || receivedChar == 'D')
          {
            receivedMessage = receivedChar; // Start building the message with the received character
          }
          if (receivedChar == 'F')
          {
            light = !light;
            change = true; // FLAG FOR LIGHT STATE CHANGE
            //Serial.println("LIGHT ON/OFF !");
          }
          if (receivedChar == 'E')
          {
            lightadjustment = true; // FLAG FOR LIGHT ADJUSTMENT
          }
        }//////////////END OF receivedMessage.length() == 0
    
        else
        {
          if (receivedChar == '!')
          {
            // Message received and ends with '!'
            Serial.println(receivedMessage);//////////////////////////////////////
            // Serial.println("MESSAGE RECEIVED, CONNECTION OK!");
            counter = millis();
            processMessage(receivedMessage, boatmode, neutral, deepdischargealarmactivation);
            receivedMessage = ""; // Clear the received message for the next one
          }
          else
          {
            receivedMessage += receivedChar;
          }
        }
      }//END OF WHILE
      /////////////////////////////////// END OF MESSAGE RECEIVING
    
      ////////////////////////////////// LIGHT ON&OFF
      if (light == true && change == true) //turn ON the light to the pre set level
      {
        change = false; //function activated only once
        lighton(); // turn ON the light function
      }
    
      if (light == false && change == true) //turn OFF the light
      {
        change = false; //function activated only once
        lightoff(); // turn the light OFF function
      }
      ////////////////////////////////// END OF LIGHT ON&OFF
    
    
      ////////////////////////////////// LIGHT LEVEL & ADJUST
      if (lightadjustment == true)
      {
        if (xy == 0)
        {
          Serial.println("MAX");
          strip.setBrightness(255);
          strip.show();
          xy = 1;
          lightadjustment = false;
        }
      }
    
      if (lightadjustment == true)
      {
        if (xy == 1)
        {
          Serial.println("DEFAULT");
          strip.setBrightness(64);
          strip.show();
          xy = 0;
          lightadjustment = false;
        }
      }
      ////////////////////////////////// END OF LIGHT LEVEL &ADJUST
    
    
      /////////////////////////////////// VOLTAGE READ AND BUZZER ACTIVATION
      if (boatmode == true)
      {
        voltageread = analogRead(A2);
        //Serial.println(voltageread);
        batvoltage = 10.88 * (long)voltageread * 1100 / 1024; //Convert voltageread to long to correctly handle multiplication (without int overflow). Voltage appplied through 1/10 divider. 0.88 after 10 for calibration
        //Serial.print("READ BATTERY VOLTAGE: ");
        //Serial.println(batvoltage);
      }
    
      //batvoltage=1.3;// BUZZER & LOW BATTERY PWM TEST
    
    
      if (batvoltage <= 6.6)
      {
        if (mediumbatlevelalarmactivation == false) //if medium battery level alarm has not been activated yet
        {
          mediumdischargecount = mediumdischargecount + 1;
          //Serial.print("MEDIUM BAT VOLTAGE COUNT:");
          //Serial.println(mediumdischargecount);
        }
      }
    
      if (batvoltage <= 5.8)
      {
        if (deepdischargealarmactivation == false) ////if battery deep discharge alarm has not been activated yet
        {
          deepdischargecount = deepdischargecount + 1;
          //Serial.print("DEEP BAT VOLTAGE COUNT:");
          //Serial.println(deepdischargecount);
        }
      }
    
      if (mediumdischargecount == 10) //medium battery level alarm activation
      {
        if (mediumbatlevelalarmactivation == false) //activate medium battery alarm only once
        {
          mediumbatlevelalarmtime = millis();
          Serial.println("MEDIUM BATTERY LEVEL ALARM !!!");
          digitalWrite(A5, HIGH); //activate buzzer
          mediumbatlevelalarmactivation = true; //medium battery alarm will not be activated again
        }
      }
    
      if (((millis() - mediumbatlevelalarmtime) > 10000 && (millis() - mediumbatlevelalarmtime) < 14000) ) //if alarm has been activated for more than 10 seconds but only once
      {
        digitalWrite(A5, LOW); //turn off the buzzer
      }
    
      if (deepdischargecount == 10) //low battery level alarm activation
      {
        digitalWrite(A5, HIGH); //activate buzzer
        if (deepdischargealarmactivation == false)
        {
          Serial.println("LOW BATTERY LEVEL ALARM !!!"); //send message only once
        }
        deepdischargealarmactivation = true;
        digitalWrite(A5, HIGH); //permanently activate the buzzer
      }
    
      /////////////////////////////////// END OF VOLTAGE READ AND BUZZER ACTIVATION
    
      //  /////////////////////////////////// VOLTAGE READ AND EEPROM READ&WRITE
      //  if (boatmode == true)
      //  {
      //    voltageread = analogRead(A2);
      //    Serial.print("ANALOG VOLTAGE: ");
      //    Serial.println(voltageread);
      //    batvoltage = 10.88 * (long)voltageread * 1100 / 1024; //Convert voltageread to long to correctly handle multiplication (without int overflow). Voltage appplied through 1/10 divider. 0.88 after 10 for calibration
      //    Serial.print("READ   VOLTAGE: ");
      //    Serial.println(batvoltage);
      //
      //    if (batvoltage > 3000) //to prevent from reading when battery is not connected (analog pin is grounded by 1k resistor)
      //    {
      //      if (batvoltage < lowestbatEEPROM) //if current voltage is lower than the lowest recorded in EEPROM
      //      {
      //        lowestbatvoltage = batvoltage;
      //        writeIntIntoEEPROM(lowestbatvoltage);
      //        Serial.print("!!!!!! NEW LOWEST VOLTAGE RECORDED: ");
      //        Serial.println(lowestbatvoltage);
      //      }
      //    }
      //
      //    Serial.print("LOWEST VOLTAGE: ");
      //    Serial.print(lowestbatvoltage);
      //    Serial.println(" (SINCE BOOT)");
      //    lowestbatEEPROM = readIntFromEEPROM(); //read lowest battery value stored in EEPROM
      //    Serial.print("LO BAT EEPROM : ");
      //    Serial.println(lowestbatEEPROM);
      //  }/////////////////////////////////// END OF VOLTAGE READ AND EEPROM READ&WRITE
    
    
    }////////////////////////////////////END OF LOOP
    
    
    
    ////////////////////////////////////FUNCTIONS///////////////////////////////////////////
    
    ///////////////////////////////////////////PROCESS MESSAGE
    void processMessage(String receivedMessage, boolean boatmode, byte bootval, boolean deepdischargealarmactivation)
    {
      //Serial.println(receivedMessage); //////////////////////////////// RECEIVED MESSAGE
    
      if (receivedMessage.charAt(0) == 'A')
      {
        af = receivedMessage.substring(1).toInt();
      }
    
      if (receivedMessage.charAt(0) == 'B')
      {
        ab = receivedMessage.substring(1).toInt();
      }
    
      if (receivedMessage.charAt(0) == 'C')
      {
        bf = receivedMessage.substring(1).toInt();
      }
    
      if (receivedMessage.charAt(0) == 'D')
      {
        bb = receivedMessage.substring(1).toInt();
      }
    
    
      if (boatmode == false) //////////////////////PWM GENERATION IF IN CAR MODE
      {
        if (af > 3) //A FORWARD PWM&LOW
        {
          analogWrite(11, af);
          digitalWrite(10, LOW);
        }
    
        if (ab > 3) //A BACKWARD PWM&LOW
        {
          digitalWrite(11, LOW);
          analogWrite(10, ab);
        }
    
        if (bf > 3) //B FORWARD PWM&LOW
        {
          analogWrite(6, bf);
          digitalWrite(5, LOW);
        }
    
        if (bb > 3) //B BACKWARD ENGINE PWM&LOW
        {
          digitalWrite(6, LOW);
          analogWrite(5, bb);
        }
    
        if (bf <= 3 && bb <= 3) //FOR ZEROING AS PROGRAM DO NOT ZERO BY DEFAULT(DO NOT INTERPRETE "0" VALUE)
        {
          digitalWrite(6, LOW);
          digitalWrite(5, LOW);
        }
    
        if (af <= 3 && ab <= 3) //FOR ZEROING AS PROGRAM DO NOT ZERO BY DEFAULT (DO NOT INTERPRETE "0" VALUE)
        {
          digitalWrite(10, LOW);
          digitalWrite(11, LOW);
        }
      } ////////////////////////END OF CAR MODE PWM GENERATION
    
      else//////////////////////PWM GENERATION IF IN BOAT MODE
      {
        if (af > 3) //A FORWARD PWM&LOW
        {
          if (deepdischargealarmactivation == false) //if deep battery discharege alarm is not activated
          {
            analogWrite(5, af);
            digitalWrite(6, LOW);
          }
          else //if deep battery discharege alarm is activated
          {
            analogWrite(5, (af / 3)); //speed reduced by 33%
            digitalWrite(6, LOW);
          }
        }
    
        if (ab > 3) //A BACKWARD PWM&LOW
        {
          if (deepdischargealarmactivation == false) //if deep battery discharege alarm is not activated
          {
            digitalWrite(5, LOW);
            analogWrite(6, ab);
          }
          else //if deep battery discharege alarm is activated
          {
            digitalWrite(5, LOW);
            analogWrite(6, (ab / 3)); //speed reduced by 33%
          }
        }
    
        if (bf > 3) //B FORWARD PWM&LOW
        {
          servoangle = map(bf, 3, 255, neutral, (neutral - 55));
          myservo.write(servoangle);
        }
    
        if (bb > 3) //B BACKWARD ENGINE PWM&LOW
        {
          servoangle = map(bb, 3, 255, neutral, (neutral + 55));
          myservo.write(servoangle);
        }
    
    
        if (af <= 3 && ab <= 3) //FOR ZEROING AS PROGRAM DO NOT ZERO BY DEFAULT (DO NOT INTERPRETE "0" VALUE)
        {
          digitalWrite(5, LOW);
          digitalWrite(6, LOW);
        }
    
        if (bf <= 3 && bb <= 3) //FOR NEUTRAL POSITIONAS PROGRAM DO NOT ZERO BY DEFAULT(DO NOT INTERPRETE "0" VALUE)
        {
          myservo.write(neutral);
        }
      } ////////////////////////END OF BOAT MODE PWM GENERATION
    
      //    FOR DEBUG ONLY. USING INTERRUPTS RECEIVED SIGNAL & OPERATION
      //    Serial.print("AF: ");
      //    Serial.println(af);
      //    Serial.print("AB: ");
      //    Serial.println(ab);
      //    Serial.print("BF: ");
      //    Serial.println(bf);
      //    Serial.print("BB: ");
      //    Serial.println(bb);
      //    FOR DEBUG ONLY. USING INTERRUPTS RECEIVED SIGNAL & OPERATION
    } /////////////////////////////////////////////////Process Message End
    
    void lighton()
    {
      strip.setBrightness(brightness);
      strip.setPixelColor(0, strip.Color(255, 255, 255));
      strip.setPixelColor(1, strip.Color(255, 255, 255));
      strip.setPixelColor(2, strip.Color(255, 0, 0));
      strip.show();
    }
    
    void lightoff()
    {
      strip.setBrightness(0);
      strip.show();
    }
    
    void startsequence() //LED BLINK AT START
    {
      digitalWrite(13, HIGH); //BUILT IN LED ON
      strip.setPixelColor(0, strip.Color(255, 255, 255));
      strip.setPixelColor(1, strip.Color(255, 255, 255));
      strip.setPixelColor(2, strip.Color(255, 0, 0));
      strip.show();
      delay(50);
      digitalWrite(13, LOW); //BUILT IN LED OFF
      strip.setBrightness(0);
      strip.show();
      delay(50);
      digitalWrite(13, HIGH); //BUILT IN LED ON
      strip.setBrightness(255);
      strip.setPixelColor(0, strip.Color(255, 255, 255));
      strip.setPixelColor(1, strip.Color(255, 255, 255));
      strip.setPixelColor(2, strip.Color(255, 0, 0));
      strip.show();
      delay(50);
      digitalWrite(13, LOW); //BUILT IN LED OFF
      strip.setBrightness(0);
      strip.show();
    }
    
    void servostart() //SEQUENCE OF SERVO MOVEMENTS AT START
    {
      myservo.write(neutral + 55);                // sets the servo position according to the scaled value
      delay(200);
      myservo.write(neutral - 55);                // sets the servo position according to the scaled value
      delay(200);
      myservo.write(neutral); // sets the servo position according to the scaled value
      delay(100);
    }
    
    void testbeep()
    {
        digitalWrite(A5, HIGH);
        delay(50);
        digitalWrite(A5, LOW);  
        delay(50);
        digitalWrite(A5, HIGH);
        delay(50);
        digitalWrite(A5, LOW);
        delay(50);
        digitalWrite(A5, HIGH);
        delay(50);
        digitalWrite(A5, LOW);  
    }
    
    boolean modecheck() //voltage check at boot to detect whether it is a boat or not
    {
      for (byte i = 0; i < 5; i++)
      {
        bootval = analogRead(A0);
        if (bootval > 600 && bootval < 700) //A0 value at USB approx. 659. 655, 665, at boat different
        {
          modecount = modecount + 1;
        }
        delay(100);
      }
    
      if (modecount == 5)
      {
        boatmode = true;
        Serial.println("BOAT MODE !");
        return boatmode;
      }
      else
      {
        Serial.println("CAR MODE !");
      }
    }// END OF MODE CHECK
    
    //void writeIntIntoEEPROM(int lowestbatvoltage)
    //{
    //  EEPROM.update(0, lowestbatvoltage >> 8); //address, variable. Shift by 8n bite, meaning saving only firts 8 bits from the digit in the first (out of two) bytes
    //  EEPROM.update(0 + 1, lowestbatvoltage & 0xFF); //For the second byte, we make an AND operation with 0xFF, which is the hexadecimal representation of 255 (you could write “& 255” instead of “& 0xFF”). This way, we only get the 8 bits on the right.
    //}
    //
    //int readIntFromEEPROM()
    //{
    //  return lowestbatEEPROM = (EEPROM.read(0) << 8) + EEPROM.read(0 + 1);
    //}
    
    //void sendping()
    //{
    //  mySerial.print("XXXXX");
    //  Serial.println("!"); //////////////////////////////// PING SENT
    //}
    Załączniki:
    • HC-12_manual.pdf (281.76 KB) Musisz być zalogowany, aby pobrać ten załącznik.
    • HC-12 Documentation and AT Commands.pdf (237.13 KB) Musisz być zalogowany, aby pobrać ten załącznik.

    Fajne? Ranking DIY
    O autorze
    Frog_Qmak
    Poziom 25  
    Offline 
    Specjalizuje się w: arduino, chemia, inżynieria przemysłowa
    Frog_Qmak napisał 1322 postów o ocenie 295, pomógł 7 razy. Mieszka w mieście Kraków. Jest z nami od 2007 roku.
  • #2 20840369
    gulson
    Administrator Systemowy
    Posty: 29271
    Pomógł: 148
    Ocena: 5993
    Bardzo Ci dziękuję za prezentacje. Dawno nie było żadnych projektów związanych z modelami RC. Napisz do mnie paczkomat i wyślę mały upominek!
  • #3 20840472
    Karol966
    Poziom 31  
    Posty: 2037
    Pomógł: 83
    Ocena: 645
    W jednym ze sklepów napisano:
    "Zasięg transmisji: od 1 m do 1000 m (poniżej 1 metra, może nie działać właściwie)", serio aż 1000 metrów można na tym uzyskać?
  • #4 20840483
    Frog_Qmak
    Poziom 25  
    Posty: 1322
    Pomógł: 7
    Ocena: 295
    Nie próbowałem - być może przy najniższym bitrate i przy zastosowaniu kierunkowych anten jest to osiągalne. Byłem pozytywnie zaskoczony, gdy okazało się, że łapie te 200 m (dalej nie próbowałem) z anteną "spiralą" na odbiorniku, częściowo schowaną w plastikowej łódce i dodatkowo zasłanianą przeze mnie (szedłem plecami do nadajnika, niosąc ją w rękach).
  • #6 20841316
    ArturAVS
    Moderator
    Posty: 26075
    Pomógł: 2295
    Ocena: 7731
    @Frog_Qmak Żabko_rechocząca :D, czy rozważałeś użycie modułów LoRa? Lub np. nRF24L01? Nazbierało mi się trochę gratów i coś bym zbudował.
  • #7 20841809
    Frog_Qmak
    Poziom 25  
    Posty: 1322
    Pomógł: 7
    Ocena: 295
    Akurat do tego radia znalazłem tutorial w internecie :) Próbowałem kiedyś z NRF, ale nie udało mi się ich uruchomić, więc wylądowały w pudle, po czym, po kilku latach trafiłem na HC :) LoRa nie próbowałem, nie wiem też, jak cenowo wyglądają :)
  • #8 20842248
    bb84
    Poziom 21  
    Posty: 715
    Pomógł: 1
    Ocena: 14
    Przydałoby się coś takiego do sterowania łodzią podwodną na 2-3 m głębokości.
  • #10 20842596
    Frog_Qmak
    Poziom 25  
    Posty: 1322
    Pomógł: 7
    Ocena: 295
    @Krzysztof Kamienski, wiem, że w środku nie powala, ;) ale nadmierne dopieszczanie nie sprawia mi satysfakcji, wolę rozwijać program, czy kolejne modele i mieć z tego frajdę, niż robić na siłę i się "wypalać".
  • #11 20843105
    OPservator
    Poziom 39  
    Posty: 6726
    Pomógł: 554
    Ocena: 1222
    Jaka prędkość maksymalna?
  • #12 20843857
    Frog_Qmak
    Poziom 25  
    Posty: 1322
    Pomógł: 7
    Ocena: 295
    Łódka (Feilun FT009) podobno 30 km/h, duże autko (Revell Aqua Crawler) podobno 20 km/h :) Łódkę dopiero będę testował na zewnątrz, ale ma konkretny silnik z chłodzeniem wodnym, bierze 8-12 A.
  • #13 20850945
    mpier
    Poziom 29  
    Posty: 817
    Pomógł: 153
    Ocena: 141
    Witam
    Modułów nie znam i w kod za bardzo nie wnikałem, ale użyty w programie sposób wysyłania może mieć (ma?) sporą wadę - duży narzut protokołu si4463. Do tego dochodzi opóźnienie samego stm8s, który nie wie ile danych wysyłasz i prawdopodobnie czeka określony czas po ostatnim bajcie, zanim prześle je do nadajnika (stąd pewnie te delay (5 ms) po transmisji.
    Jeśli również odbierasz po jednym kanale i każdorazowo aktualizujesz timer od ostatniego odebranego pakietu, to istnieje niewielka szansa, że uda Ci się wysłać tylko chwilowo mniej ważne pakiety, a te potrzebne nie dolecą, a odbiornik tego nie zauważy (np. nie dolecą te o hamowaniu).

    Myślę, że wysyłając wszystko razem i dobierając odpowiednią długość danych (pewnie zostanie miejsce na sumę kontrolną i może numer pakietu) możesz zwiększyć szybkość sterowania, skuteczność wykrywania błędów i jednocześnie zmniejszyć zajętość pasma (średni czas transmisji raczej sporo spadnie). Mając numer pakietu możesz określić jakość sygnału i zareagować np. tak jak przy słabej baterii.

    Przy mniejszych autkach ten moduł hc12 powinien poradzić sobie bez Arduino jeśli go przeprogramujesz.

    Pozdrawiam
  • #14 20850977
    Frog_Qmak
    Poziom 25  
    Posty: 1322
    Pomógł: 7
    Ocena: 295
    Hej, dziękuję za cenną uwagę.
    W przypadku dużego modelu (który mógłby np. swoją masą stanowić zagrożenie), lub innej aplikacji, gdzie ważna jest niezawodność, trzeba by było zwrócić na to uwagę.
    W moim konkretnym przypadku pilot wysyła dane non-stop, więc jeżeli joystick znajdzie się w pozycji neutralnej, to odbiornik otrzyma ten pakiet (nawet, jeśli nie za pierwszym razem).
    Wcześniej borykałem się z opisanym przez Ciebie problemem (długość wiadomości - wszystkie położenia wysyłane w jednym komunikacie), natomiast zoptymalizowałem kod i teraz wiadomości są znacznie krótsze.
    Dobry pomysł z tym numerowaniem pakietu w celu oceny jakości sygnału - muszę go zapamiętać!
  • #15 21046407
    Frog_Qmak
    Poziom 25  
    Posty: 1322
    Pomógł: 7
    Ocena: 295
    Witajcie,

    dodaję zaktualizowane (ulepszone) wersje softu dla nadajnika i odbiornika.


    Główne zmiany:
    1. Dodałem obsługę regulatorów modelarskich silników (ESC), alternatywnie do mostków H. Miałem mnóstwo problemów z palącymi się mostkami H (silnik pobiera bądź co bądź 10...13A, więc generuje sporo przepięć*), testując różne rozwiązania straciłem kilka miesięcy, stanęło na regulatorze Redox Ultra 45. Ważne jest, że regulator przy starcie trzeba "uzbroić", symulując neutralną pozycję drążka przy starcie i odpowiednią sekwencję ruchów (max dół, max góra, neutral). Miałem po drodze jeszcze jednego "chińczyka" 20 A (mnóstwo takich na Allegro), testowałem wiele kombinacji, żadna nie zadziałała. Redox ruszył od razu.
    2. W nadajniku dodałem drugi znak "!" na końcu wiadomości. Często pojedynczy "się gubił" (przypuszczam, że mogła to być kwestia dalszej optymalizacji kodu odbiornika, albo jakichś konfliktów w jego timerach), powodując niestabilność pracy ("drgania" serwa i mikroprzerwy pracy silnika). Drugi znak niczemu nie szkodzi a problemy zniknęły od ręki.
    3. Różne drobne optymalizacje kodu i jego czytelności czytelności.

    *upaliłem w ten sposób wiele mostków, a także wbudowany w Arduino stabilizator. Nie pomogły transile ani dodatkowe kondensatory. Dla ciekawych podsyłam LINK do wątku.

    NADAJNIK
    
    //! ends the message. 2x ! are used, because sometimes single "!" was lost causing receiver merge two messages together which resulted in noise.
    
    #include <SoftwareSerial.h>
    SoftwareSerial mySerial(2, 3); //RX, TX
    
    //L: 3-507-1017 --> 1020 for smooth operation [506;508]
    //R: 3-526-1017 --> 1020 for smooth operation [525;527]
    //STEER [R]: 0-517v518-1022 [521;523]
    int ax; //analog read joystick A
    byte af; //analog joystick A forward
    byte ab; //analog joystick A backward
    
    int bx; //analog read joystick B
    byte bf; //analog joystick B forward
    byte bb; //analog joystick B backward
    
    boolean mode = false; //Tank or Car mode (default: car)
    int buttontest; //conunting time of button being pressed
    
    String toSend;
    
    
    void setup()
    {
      pinMode(4, OUTPUT);
      pinMode(12, OUTPUT);
      pinMode(11, OUTPUT);
      pinMode(10, OUTPUT);
      pinMode(9, OUTPUT);
      pinMode(5, OUTPUT);
      pinMode(6, OUTPUT);
    
      digitalWrite(4, HIGH); //DISABLE AT MODE
      digitalWrite(12, HIGH);//RIGHT JOYSTICK VCC
      digitalWrite(11, LOW);//RIGHT JOYSTICK GND
      digitalWrite(10, HIGH);//LEFT JOYSTICK VCC
      digitalWrite(9, LOW);//LEFT JOYSTICK GND
      digitalWrite(5, LOW);//BUZZER GND
      digitalWrite(6, LOW);//BUZZER VCC
    
      pinMode(A0, INPUT); //LEFT JOYSTICK BUTTON
      pinMode(A1, INPUT); //RIGHT JOYSTICK BUTTON
      pinMode(A5, INPUT); //RIGHT JOYSTICK FORWARD & BACKWARD
      pinMode(A6, INPUT); //RIGHT JOYSTICK RIGHT & LEFT
      pinMode(A2, INPUT); //LEFT JOYSTICK FORWARD & BACKWARD
    
      Serial.begin(9600);
      pinMode(4, OUTPUT); //for MODE setup
      digitalWrite(4, LOW); //ENTER AT MODE
      delay(50);//necessary to wait after mode change
      mySerial.begin(9600);//9600 adjust after adjusting communication speed using AT+BXXX
      mySerial.println("AT+FU3");
      delay(50);
      mySerial.println("AT+B9600");//9600
      delay(50);
      mySerial.println("AT+RX"); //AT MODE REQUIRED TO BE ON (4 LOW)
      delay(100);
      digitalWrite(4, HIGH); //EXIT AT MODE
      delay(50);//necessary to wait after mode change
    }
    
    
    
    void loop()
    {
      //RESET OLD VALUES TO AVOID CONFLICTS
      af = 0;
      ab = 0;
      bf = 0;
      bb = 0;
    
    
    
      ////////////////////////////////STEERING MODE CHECK
      if (analogRead(A0) < 10) //BUTTON PRESSED
    
      {
        buttontest = buttontest + 1; //COUNTING TIME OF BUTTON BEING PRESSED
        mySerial.print("BUTTON PRESSED !");
        Serial.println("BUTTON PRESSED !");
      }
      else
      {
        buttontest = 0; //RESET IN CASE OF ACCIDENTAL CLICK
      }
    
      //Serial.println(buttontest);
    
      if (buttontest == 40 ) //BUTTON PRESSED - SENT LIGHT ADJUST SIGNAL
      {
        toSend = String("E") + "!"; 
        mySerial.print(toSend);
        delay(1);
        digitalWrite(6, HIGH);
        delay(100);
        digitalWrite(6, LOW);
        delay(100);
        digitalWrite(6, HIGH);
        delay(100);
        digitalWrite(6, LOW);
        delay(1000);
      }
    
    
      if (buttontest == 41) //BUTTON PRESSED - SENT LIGHT ON/OFF SIGNAL
      {
        toSend = toSend = String("F") + "!"; 
        mySerial.print(toSend);
        delay(1);
        digitalWrite(6, HIGH);
        delay(1000);
        digitalWrite(6, LOW);
      }
    
    
    
      if (buttontest >= 42) //BUTTON PRESSED - CHANGE TO TANK MODE
      {
        mode = !mode;
        buttontest = 0;
        mySerial.print("MODE CHANGED !");
        delay(1);
        Serial.println("MODE CHANGED !");
        digitalWrite(6, HIGH);
        delay(4000);
        digitalWrite(6, LOW);
      }
    
    
    
      ////////////////////////////////////////LEFT JOYSTICK READ & CALCULATE
      ax = analogRead(A2); //LEFT JOYSTICK
      //  if (mode == 1)
      //  {
      //    Serial.print("A2 LEFT:  ");
      //    Serial.println(ax);
      //  }
      //  else
      //  {
      //    Serial.print("A2 MOVE: ");
      //    Serial.println(ax);
      //  }
    
    
    
    //Serial.print("AX READ: ");
    //Serial.println(ax);
    
      //af is PWM for A engine forward
      if (ax > 508)
      {
        af = map(ax, 508, 1020, 0, 255); //af is PWM for A engine forward
        //Serial.print("LEFT FORWARD: ");
        //Serial.println(af);
        toSend = "A" + String(af, DEC) + "!!"; 
        mySerial.print(toSend);
        delay(5);
      }
    
    if (ax >= 506 && ax <=508) //to send zero command in case of neutral joistick position
    {
         toSend = "A" + String(0, DEC) + "!!";  
         mySerial.print(toSend);
         delay(5);
         toSend = "B" + String(0, DEC) + "!!";  
         mySerial.print(toSend);
         delay(5);
    }
    
      if (ax < 506)
      {
        ab = map(ax, 3, 506, 255, 0); //ab is PWM for A engine backward
        //Serial.print("LEFT BACKWARD: ");
        //Serial.println(ab);
        toSend = "B" + String(ab, DEC) + "!!"; 
        mySerial.print(toSend);
        delay(5);
      }
      ////////////////////////////////END OF LEFT JOYSTICK READ & CALCULATE
    
    
    
      ////////////////////////////////////////RIGHT JOYSTICK READ & CALCULATE
      ///////////READING POSITION FROM CORRECT JOYSTICK OUTPUT
      if (mode == 1)
      {
        bx = analogRead(A5); //IF IN TANK MODE
        //    Serial.print("A5 RIGHT: ");
        //   Serial.println(bx);
      }
      else
      {
        bx = analogRead(A4); //IF IN CAR MODE
        //Serial.print("A6 STEER: ");
        //Serial.println(bx);
      }
      ///////////END OF READING FROM CORRECT JOYSTICK OUTPUT
    
      ///////////CALCULATING PWM ACCORDING TO
      //JOYSTICK'S ZERO CALIBRATION POSITION
      if (mode == 1) //IF IN TANK MODE
      {
        if (bx > 527)
        {
          bf = map(bx, 527, 1020, 0, 255); // bf is PWM for B engine forward
          //Serial.print("RIGHT FORWARD: ");
          //Serial.println(bf);
          toSend = "C" + String(bf, DEC) + "!!"; 
          mySerial.print(toSend);
          delay(5);
        }
    
        if (bx < 525)
        {
          bb = map(bx, 3, 527, 255, 0); //bb is PWM for B engine backward
          //Serial.print("RIGHT BACKWARD: ");
          //Serial.println(bb);
          toSend = "D" + String(bb, DEC) + "!!"; 
          mySerial.print(toSend);
          delay(5);
        }
      }
      else //IF IN CAR MODE
      {
        if (bx > 520)
        {
          bf = map(bx, 517, 1022, 0, 255); //bf is PWM for B engine forward
          //Serial.print("STEER RIGHT: ");
          //Serial.println(bf);
          toSend = "C" + String(bf, DEC) + "!!"; 
          mySerial.print(toSend);
          //Serial.println(toSend);
          delay(5);
        }
    
    if (bx >= 515 && bx <=520) //to send zero command in case of neutral joistick position
    {   
         toSend = "C" + String(0, DEC) + "!!";  
         mySerial.print(toSend);
         delay(5);
         toSend = "D" + String(0, DEC) + "!!";  
         mySerial.print(toSend);
         delay(5);
    }
    
        if (bx < 515)
        {
          bb = map(bx, 0, 517, 255, 0); //bb is PWM for B engine backward
          //Serial.print("STEER LEFT: ");
          //Serial.println(bb);
          toSend = "D" + String(bb, DEC) + "!!"; 
          mySerial.print(toSend);
          //Serial.println(toSend);
          delay(5);
        }
      }
    
      ////////////////RIGHT JOYSTICK DIAG
    //  Serial.print("AX: ");
    //  Serial.println(ax);
    //  Serial.print("BX: ");
    //  Serial.println(bx);
      /////////////////////////////////END OF LEFT JOYSTICK READ & CALCULATE
    
    
    }// END OF LOOP
    


    ODBIORNIK
    
    #include <SoftwareSerial.h>
    #include <Adafruit_NeoPixel.h>
    //#include <EEPROM.h>
    #include <PWMServo.h>//software PWM used because servo library had conflict with other used functions using PWM generated by the same internal clock
    PWMServo myservo;  // create servo object to control a servo
    PWMServo esc;  // create servo object to control an ESC (motor controller)
    SoftwareSerial mySerial(2, 3); //RX, TX
    Adafruit_NeoPixel
    strip = Adafruit_NeoPixel(3, 13, NEO_GRB + NEO_KHZ800); //NO of pixels, PIN
    byte af, ab, bf, bb;
    boolean light = 0; //LIGHT ON/OFF, default = OFF
    boolean change = false; // flag to mark change to use WS2812B communication once for light ON/OFF. Constant communication interrupts message receiving
    boolean lightadjustment = false; // flag to mark change to use WS2812B communication once for light level adjustment. Constant communication interrupts message receiving
    byte xy; //light brightness pre set setting
    byte brightness = 64; //brightness level (0-255), default = 64
    boolean boatmode;// if true, then boat mode
    byte modecount = 0; //no. of successful read sequences
    int  bootval;// voltage check at boot to detect whether it is a boat or not
    byte neutral = 85; //neutral position of servo
    byte servoangle; //servo's angle
    byte escangle;// mapped value to be sent as "servo" output to ESC
    int serwoPWM;// calculated value to steer servo by generating PWM
    unsigned long counter; //time from last message
    unsigned long nowtime; //time now. Used further to calculate time past since previous loop
    //unsigned long pinginterval; // for signalling range ping messages
    float voltageread; //voltage read at input
    float batvoltage; //calculated battery level (incl. calculated offset)
    byte mediumdischargecount = 0; //to avoid one - time battery voltage drops,alarm will be activated after few times threshold is reached
    byte deepdischargecount = 0; //to avoid one - time battery voltage drops,alarm will be activated after few times threshold is reached
    long mediumbatlevelalarmtime; //time of buzzer's activation
    boolean mediumbatlevelalarmactivation = false;
    boolean deepdischargealarmactivation = false;
    
    //int lowestbatvoltage; ////////////////////USED ONLY AT BOOT TO HAVE ANY VALUE FOR IF FUNCTION TO COMPARE TO
    //int lowestbatEEPROM;
    
    
    
    void setup()
    {
      //writeIntIntoEEPROM(7000);//EEPROM LOW RESET USED TO START A NEW RECORDING
      //PIN D11 BURNT !!!!!!!!!!!!
      //PINS A6&A7 cannot be used as analog output in Arduino Nano
      pinMode(4, OUTPUT); //AT MODE SWITCH
      pinMode(5, OUTPUT);
      pinMode(6, OUTPUT);
      pinMode(10, OUTPUT);//SERVO
      pinMode(13, OUTPUT); //built in led + WS2812B
      pinMode(A1, OUTPUT); //GND for voltage sensing
      pinMode(A0, INPUT); //voltage check at boot to detect whether it is a boat or not
      pinMode(A2, INPUT); //battery voltage check
      pinMode(A5, OUTPUT); //low battery buzzer output
      digitalWrite(A1, LOW); //GND for voltage sensing
      digitalWrite(A5, LOW); //deactivate buzzer
      digitalWrite(4, LOW); //ENTER AT MODE
      delay(50);//necessary to wait after AT mode change
    
      Serial.begin(9600);
      mySerial.begin(9600);//adjust after adjusting communication speed using AT+BXXX
      mySerial.println("AT+FU3");
      delay(50);
      mySerial.println("AT+B9600");
      delay(50);
      mySerial.println("AT+RX"); //AT MODE REQUIRED TO BE ON (4 LOW)
      delay(100);
      digitalWrite(4, HIGH); //EXIT AT MODE
      delay(50);//necessary to wait after mode change\
      analogReference(INTERNAL); //1,1V
      modecheck(); //Function to detect whether hardware is a boat or not
    
      analogReference(INTERNAL); //1,1V for battery measurement
    
      if (boatmode == true)
      {
        myservo.attach(SERVO_PIN_B); //Library works only at PIN9 and 10 - rudder 10
        esc.attach(SERVO_PIN_A); //Library works only at PIN9 and 10 - motor controller 9
        //ESCinit(); //ARM ESC (MOTOR CONTROLLER)///////////////////////////////////////
        servostart(); //Sequence for servo movement at boat's boot
        testbeep(); //Test buzzer
        //Serial.print("LOWEST VOLTAGE FROM EEPROM: ");
        //Serial.println(readIntFromEEPROM());
      }
      else
      {
        strip.begin(); // INITIALIZE NeoPixel strip object
        strip.clear();
        strip.show();
        startsequence(); //LED BLINK at car's boot
      }
    
    
    } ///////////////////////////////////////////END OF SETUP
    
    
    void loop()
    {
    
    //  esc.write(120);
    //  delay(250);
    //  esc.write(70);
    //  delay(250);
      
      ///////////////////////LOST CONNECTION PROTECTION
      nowtime = millis();
      if ((nowtime - counter) > 1000) //1000 MS SIGNAL LOST TRIGGER
      {
        //Serial.println(nowtime-counter);
        //Serial.println("CONNECTION LOST !!!");
        if (boatmode == false) //IF IN CAR MODE
        {
          digitalWrite(11, LOW);
          digitalWrite(10, LOW);
          digitalWrite(6, LOW);
          digitalWrite(5, LOW);
        }
        else //IF IN BOAT MODE
        {
          digitalWrite(6, LOW);
          digitalWrite(5, LOW);
        }
      }
      ///////////////////////END OF LOST CONNECTION PROTECTION
    
      //////////////////////////100 MS SIGNAL PING TRIGGER
      //    if ((nowtime - pinginterval) > 50)
      //    {
      //      //Serial.println("PING INTERVAL");
      //      //Serial.println(nowtime-pinginterval);//Loop time
      //      pinginterval = millis();
      //      ();
      //    }
      ///////////////////////END OF SIGNAL PING TRIGGER
    
    
      ////////////////////////////////////// MESSAGE RECEIVING
      while (mySerial.available())
      {
        static String receivedMessage = ""; // Store the received message
        char receivedChar = mySerial.read();
    
        if (receivedMessage.length() == 0)
        {
          if (receivedChar == 'A' || receivedChar == 'B' || receivedChar == 'C' || receivedChar == 'D')
          {
            receivedMessage = receivedChar; // Start building the message with the received character
          }
          if (receivedChar == 'F')
          {
            light = !light;
            change = true; // FLAG FOR LIGHT STATE CHANGE
            //Serial.println("LIGHT ON/OFF !");
          }
          if (receivedChar == 'E')
          {
            lightadjustment = true; // FLAG FOR LIGHT ADJUSTMENT
          }
        }//////////////END OF receivedMessage.length() == 0
    
        else
        {
          if (receivedChar == '!')
          {
            // Message received and ends with '!'
            Serial.println(receivedMessage);//////////////////////////////////////
            // Serial.println("MESSAGE RECEIVED, CONNECTION OK!");
            counter = millis();
            processMessage(receivedMessage, boatmode, neutral, deepdischargealarmactivation);
            receivedMessage = ""; // Clear the received message for the next one
          }
          else
          {
            receivedMessage += receivedChar;
          }
        }
      }//END OF WHILE
      /////////////////////////////////// END OF MESSAGE RECEIVING
    
      ////////////////////////////////// LIGHT ON&OFF
      if (light == true && change == true) //turn ON the light to the pre set level
      {
        change = false; //function activated only once
        lighton(); // turn ON the light function
      }
    
      if (light == false && change == true) //turn OFF the light
      {
        change = false; //function activated only once
        lightoff(); // turn the light OFF function
      }
      ////////////////////////////////// END OF LIGHT ON&OFF
    
    
      ////////////////////////////////// LIGHT LEVEL & ADJUST
      if (lightadjustment == true)
      {
        if (xy == 0)
        {
          Serial.println("MAX");
          strip.setBrightness(255);
          strip.show();
          xy = 1;
          lightadjustment = false;
        }
      }
    
      if (lightadjustment == true)
      {
        if (xy == 1)
        {
          Serial.println("DEFAULT");
          strip.setBrightness(64);
          strip.show();
          xy = 0;
          lightadjustment = false;
        }
      }
      ////////////////////////////////// END OF LIGHT LEVEL &ADJUST
    
    
      /////////////////////////////////// VOLTAGE READ AND BUZZER ACTIVATION
      if (boatmode == true)
      {
        voltageread = analogRead(A2);
        //Serial.println(voltageread);
        batvoltage = 10.88 * (long)voltageread * 1100 / 1024; //Convert voltageread to long to correctly handle multiplication (without int overflow). Voltage appplied through 1/10 divider. 0.88 after 10 for calibration
        //Serial.print("READ BATTERY VOLTAGE: ");
        //Serial.println(batvoltage);
      }
    
      //batvoltage=1.3;// BUZZER & LOW BATTERY PWM TEST
    
    
      if (batvoltage <= 6.6)
      {
        if (mediumbatlevelalarmactivation == false) //if medium battery level alarm has not been activated yet
        {
          mediumdischargecount = mediumdischargecount + 1;
          //Serial.print("MEDIUM BAT VOLTAGE COUNT:");
          //Serial.println(mediumdischargecount);
        }
      }
    
      if (batvoltage <= 5.8)
      {
        if (deepdischargealarmactivation == false) ////if battery deep discharge alarm has not been activated yet
        {
          deepdischargecount = deepdischargecount + 1;
          //Serial.print("DEEP BAT VOLTAGE COUNT:");
          //Serial.println(deepdischargecount);
        }
      }
    
      if (mediumdischargecount == 10) //medium battery level alarm activation
      {
        if (mediumbatlevelalarmactivation == false) //activate medium battery alarm only once
        {
          mediumbatlevelalarmtime = millis();
          Serial.println("MEDIUM BATTERY LEVEL ALARM !!!");
          digitalWrite(A5, HIGH); //activate buzzer
          mediumbatlevelalarmactivation = true; //medium battery alarm will not be activated again
        }
      }
    
      if (((millis() - mediumbatlevelalarmtime) > 10000 && (millis() - mediumbatlevelalarmtime) < 14000) ) //if alarm has been activated for more than 10 seconds but only once
      {
        digitalWrite(A5, LOW); //turn off the buzzer
      }
    
      if (deepdischargecount == 10) //low battery level alarm activation
      {
        digitalWrite(A5, HIGH); //activate buzzer
        if (deepdischargealarmactivation == false)
        {
          Serial.println("LOW BATTERY LEVEL ALARM !!!"); //send message only once
        }
        deepdischargealarmactivation = true;
        digitalWrite(A5, HIGH); //permanently activate the buzzer
      }
    
      /////////////////////////////////// END OF VOLTAGE READ AND BUZZER ACTIVATION
    
      //  /////////////////////////////////// VOLTAGE READ AND EEPROM READ&WRITE
      //  if (boatmode == true)
      //  {
      //    voltageread = analogRead(A2);
      //    Serial.print("ANALOG VOLTAGE: ");
      //    Serial.println(voltageread);
      //    batvoltage = 10.88 * (long)voltageread * 1100 / 1024; //Convert voltageread to long to correctly handle multiplication (without int overflow). Voltage appplied through 1/10 divider. 0.88 after 10 for calibration
      //    Serial.print("READ   VOLTAGE: ");
      //    Serial.println(batvoltage);
      //
      //    if (batvoltage > 3000) //to prevent from reading when battery is not connected (analog pin is grounded by 1k resistor)
      //    {
      //      if (batvoltage < lowestbatEEPROM) //if current voltage is lower than the lowest recorded in EEPROM
      //      {
      //        lowestbatvoltage = batvoltage;
      //        writeIntIntoEEPROM(lowestbatvoltage);
      //        Serial.print("!!!!!! NEW LOWEST VOLTAGE RECORDED: ");
      //        Serial.println(lowestbatvoltage);
      //      }
      //    }
      //
      //    Serial.print("LOWEST VOLTAGE: ");
      //    Serial.print(lowestbatvoltage);
      //    Serial.println(" (SINCE BOOT)");
      //    lowestbatEEPROM = readIntFromEEPROM(); //read lowest battery value stored in EEPROM
      //    Serial.print("LO BAT EEPROM : ");
      //    Serial.println(lowestbatEEPROM);
      //  }/////////////////////////////////// END OF VOLTAGE READ AND EEPROM READ&WRITE
    
    
    }////////////////////////////////////END OF LOOP
    
    
    
    ////////////////////////////////////FUNCTIONS///////////////////////////////////////////
    
    ///////////////////////////////////////////PROCESS MESSAGE
    void processMessage(String receivedMessage, boolean boatmode, byte bootval, boolean deepdischargealarmactivation)
    {
      //Serial.println(receivedMessage); //////////////////////////////// RECEIVED MESSAGE
    
      if (receivedMessage.charAt(0) == 'A')
      {
        af = receivedMessage.substring(1).toInt();
      }
    
      if (receivedMessage.charAt(0) == 'B')
      {
        ab = receivedMessage.substring(1).toInt();
      }
    
      if (receivedMessage.charAt(0) == 'C')
      {
        bf = receivedMessage.substring(1).toInt();
      }
    
      if (receivedMessage.charAt(0) == 'D')
      {
        bb = receivedMessage.substring(1).toInt();
      }
    
    
      if (boatmode == false) //////////////////////PWM GENERATION IF IN CAR MODE
      {
        if (af > 3) //A FORWARD PWM&LOW
        {
          analogWrite(11, af);
          digitalWrite(10, LOW);
        }
    
        if (ab > 3) //A BACKWARD PWM&LOW
        {
          digitalWrite(11, LOW);
          analogWrite(10, ab);
        }
    
        if (bf > 3) //B FORWARD PWM&LOW
        {
          analogWrite(6, bf);
          digitalWrite(5, LOW);
        }
    
        if (bb > 3) //B BACKWARD ENGINE PWM&LOW
        {
          digitalWrite(6, LOW);
          analogWrite(5, bb);
        }
    
        if (bf <= 3 && bb <= 3) //FOR ZEROING AS PROGRAM DO NOT ZERO BY DEFAULT(DO NOT INTERPRETE "0" VALUE)
        {
          digitalWrite(6, LOW);
          digitalWrite(5, LOW);
        }
    
        if (af <= 3 && ab <= 3) //FOR ZEROING AS PROGRAM DO NOT ZERO BY DEFAULT (DO NOT INTERPRETE "0" VALUE)
        {
          digitalWrite(10, LOW);
          digitalWrite(11, LOW);
        }
      } ////////////////////////END OF CAR MODE PWM GENERATION
    
      else//////////////////////PWM GENERATION IF IN BOAT MODE
      {
    
    
        ////////////////////////////////////////////////////////////IF ESC CONTROLLER USED
        if (af > 3) //A FORWARD ESC
        {
          if (deepdischargealarmactivation == false) //if deep battery discharege alarm is not activated
          {
            escangle = map(af, 3, 255, 90, 180);
            esc.write(escangle);
          }
          else //if deep battery discharege alarm is activated
          {
            escangle = map(af, 3, 255, 90, 120); //max speed reduced to 1/3
            esc.write(escangle);
          }
        }
    
        if (ab > 3) //A BACKWARD ESC
        {
          if (deepdischargealarmactivation == false) //if deep battery discharege alarm is not activated
          {
            escangle = map(ab, 3, 255, 90, 10); //cannot be "0", issue with ESC (too low PWM signal?)
            esc.write(escangle);
          }
          else //if deep battery discharege alarm is activated
          {
            escangle = map(ab, 3, 255, 90, 70); //max speed reduced to 1/3
            esc.write(escangle);
          }
        }
            ////////////////////////////////////////////////////////////IF ESC CONTROLLER USED
        
        ////////////////////////////////////////////////////////////IF PWM CONTROLLER USED
    //    if (af > 3) //A FORWARD PWM&LOW
    //    {
    //      if (deepdischargealarmactivation == false) //if deep battery discharege alarm is not activated
    //      {
    //        analogWrite(5, af);
    //        digitalWrite(6, LOW);
    //      }
    //      else //if deep battery discharege alarm is activated
    //      {
    //        analogWrite(5, (af / 3)); //speed reduced by 33%
    //        digitalWrite(6, LOW);
    //      }
    //    }
    //
    //    if (ab > 3) //A BACKWARD PWM&LOW
    //    {
    //      if (deepdischargealarmactivation == false) //if deep battery discharege alarm is not activated
    //      {
    //        digitalWrite(5, LOW);
    //        analogWrite(6, ab);
    //      }
    //      else //if deep battery discharege alarm is activated
    //      {
    //        digitalWrite(5, LOW);
    //        analogWrite(6, (ab / 3)); //speed reduced by 33%
    //      }
    //    }
            ////////////////////////////////////////////////////////////IF PWM CONTROLLER USED
    
        if (bf > 3) //B FORWARD PWM&LOW -->here SERVO
        {
          servoangle = map(bf, 3, 255, neutral, (neutral - 55));
          myservo.write(servoangle);
        }
    
        if (bb > 3) //B BACKWARD ENGINE PWM&LOW --> here SERVO
        {
          servoangle = map(bb, 3, 255, neutral, (neutral + 55));
          myservo.write(servoangle);
        }
    
    
        if (af <= 3 && ab <= 3) //FOR ZEROING AS PROGRAM DO NOT ZERO BY DEFAULT (DO NOT INTERPRETE "0" VALUE)
        {
          esc.write(90);//if ESC used
          //digitalWrite(5, LOW); //IF PWM CONTROLLER USED
          //digitalWrite(6, LOW); //IF PWM CONTROLLER USED
        }
    
        if (bf <= 3 && bb <= 3) //FOR NEUTRAL POSITIONAS PROGRAM DO NOT ZERO BY DEFAULT(DO NOT INTERPRETE "0" VALUE)
        {
          myservo.write(neutral);
        }
      } ////////////////////////END OF BOAT MODE PWM GENERATION
    
      //    FOR DEBUG ONLY. USING INTERRUPTS RECEIVED SIGNAL & OPERATION
      //    Serial.print("AF: ");
      //    Serial.println(af);
      //    Serial.print("AB: ");
      //    Serial.println(ab);
      //    Serial.print("BF: ");
      //    Serial.println(bf);
      //    Serial.print("BB: ");
      //    Serial.println(bb);
      //    FOR DEBUG ONLY. USING INTERRUPTS RECEIVED SIGNAL & OPERATION
    } /////////////////////////////////////////////////Process Message End
    
    void lighton()
    {
      strip.setBrightness(brightness);
      strip.setPixelColor(0, strip.Color(255, 255, 255));
      strip.setPixelColor(1, strip.Color(255, 255, 255));
      strip.setPixelColor(2, strip.Color(255, 0, 0));
      strip.show();
    }
    
    void lightoff()
    {
      strip.setBrightness(0);
      strip.show();
    }
    
    void startsequence() //LED BLINK AT START
    {
      digitalWrite(13, HIGH); //BUILT IN LED ON
      strip.setPixelColor(0, strip.Color(255, 255, 255));
      strip.setPixelColor(1, strip.Color(255, 255, 255));
      strip.setPixelColor(2, strip.Color(255, 0, 0));
      strip.show();
      delay(50);
      digitalWrite(13, LOW); //BUILT IN LED OFF
      strip.setBrightness(0);
      strip.show();
      delay(50);
      digitalWrite(13, HIGH); //BUILT IN LED ON
      strip.setBrightness(255);
      strip.setPixelColor(0, strip.Color(255, 255, 255));
      strip.setPixelColor(1, strip.Color(255, 255, 255));
      strip.setPixelColor(2, strip.Color(255, 0, 0));
      strip.show();
      delay(50);
      digitalWrite(13, LOW); //BUILT IN LED OFF
      strip.setBrightness(0);
      strip.show();
    }
    
    void servostart() //SEQUENCE OF SERVO MOVEMENTS AT START
    {
      myservo.write(neutral + 55);                // sets the servo position according to the scaled value
      delay(200);
      myservo.write(neutral - 55);                // sets the servo position according to the scaled value
      delay(200);
      myservo.write(neutral); // sets the servo position according to the scaled value
      delay(100);
    }
    
    void testbeep()
    {
      digitalWrite(A5, HIGH);
      delay(50);
      digitalWrite(A5, LOW);
      delay(50);
      digitalWrite(A5, HIGH);
      delay(50);
      digitalWrite(A5, LOW);
      delay(50);
      digitalWrite(A5, HIGH);
      delay(50);
      digitalWrite(A5, LOW);
    }
    
    void shortbeep()
    {
      digitalWrite(A5, HIGH);
      delay(25);
      digitalWrite(A5, LOW);
    }
    
    boolean modecheck() //voltage check at boot to detect whether it is a boat or not
    {
      for (byte i = 0; i < 5; i++)
      {
        bootval = analogRead(A0);
        if (bootval > 600 && bootval < 700) //A0 value at USB approx. 659. 655, 665, at boat different
        {
          modecount = modecount + 1;
        }
        delay(100);
      }
    
      if (modecount == 5)
      {
        boatmode = true;
        Serial.println("BOAT MODE !");
        return boatmode;
      }
      else
      {
        Serial.println("CAR MODE !");
      }
    }// END OF MODE CHECK
    
    void ESCinit()
    {
      byte pos = 0;
      esc.write(0);
    
      for (pos = 0; pos < 181; pos += 1)
      {
        esc.write(pos);
        delay(10);
        //Serial.println(pos);
      }
      shortbeep();
      delay(200);
    
      for (pos = 180; pos < 181; pos -= 1)
      {
        esc.write(pos);
        delay(10);
        //Serial.println(pos);
      }
      shortbeep();
      delay(200);
      for (pos = 0; pos < 91; pos += 1)
      {
        esc.write(pos);
        delay(10);
        //Serial.println(pos);
      }
      shortbeep();
      delay(200);
    }
    
    //void writeIntIntoEEPROM(int lowestbatvoltage)
    //{
    //  EEPROM.update(0, lowestbatvoltage >> 8); //address, variable. Shift by 8n bite, meaning saving only firts 8 bits from the digit in the first (out of two) bytes
    //  EEPROM.update(0 + 1, lowestbatvoltage & 0xFF); //For the second byte, we make an AND operation with 0xFF, which is the hexadecimal representation of 255 (you could write “& 255” instead of “& 0xFF”). This way, we only get the 8 bits on the right.
    //}
    //
    //int readIntFromEEPROM()
    //{
    //  return lowestbatEEPROM = (EEPROM.read(0) << 8) + EEPROM.read(0 + 1);
    //}
    
    //void sendping()
    //{
    //  mySerial.print("XXXXX");
    //  Serial.println("!"); //////////////////////////////// PING SENT
    //}
    
  • #16 21046953
    Slawek K.
    Poziom 35  
    Posty: 3019
    Pomógł: 259
    Ocena: 1300
    Trudno nazwać to zoptymalizowaniem, dopóki nie wywalisz mnóstwa delay z kodu, zwłaszcza odbiornika. Zrób takie ćwiczenie i wysyłaj w ramce za każdym razem inkrementowany licznik wysyłanych ramek po stronie nadajnika i zwiększaj licznik po stronie odbiornika ramek odebranych, następnie porównaj po stronie odbiornika oba liczniki i zobaczysz ile jaką masz różnicę :)

    Pozdr
  • #17 21048100
    Frog_Qmak
    Poziom 25  
    Posty: 1322
    Pomógł: 7
    Ocena: 295
    Dzięki za komentarz, to fakt, że kod można by jeszcze zoptymalizować, ale wymagało by to sporych przeróbek. Póki co działa bez zarzutu, zasięg bardziej, niż wystarczający, więc nie ma potrzeby, by go rozgrzebywać. Wstawka z obsługą ESC może się komuś przydać, sporo się naczytałem, jak do tego podejść, projekt zyskuje w ten sposób na uniwersalności.
  • #18 21120072
    Frog_Qmak
    Poziom 25  
    Posty: 1322
    Pomógł: 7
    Ocena: 295
    Witajcie,

    dodaję kolejną rewizję kodu odbiornika. Oprócz drobnych usprawnień, pojawiły się dwie istotne zmiany.

    1. Jako że łódka jest BARDZO zwrotna, tzn. przy wysokiej prędkości i wychyleniu steru niemalże się zatrzymuje i zawraca w miejscu, dodałem ogranicznik wychylenia steru, tzn. przy pewnym wychyleniu joysticka odpowiadający mu ruch serwa jest osłabiony, natomiast dla zachowania wysokiej manewrowości przy pozycji bliskiej krańcowej, serwo znów reaguje w gwałtowny sposób.

    2. Jako że obracająca się z ogromną prędkością śruba powoduje wychylenie łódki wzdłuż osi, a przez to skręcanie, dodałem "offset" z możliwością jego kalibracji. Działa to w ten sposób, że na pilocie wychyla się joystick odpowiadający kierunkowi, w którym powinien wychylić się ster, a następnie wciska i przytrzymuje się przycisk na drugim joysticku (jak do zmiany oświetlenia w przypadku samochodu). Powoduje do zmianę "offsetu" o 1 (pozycję serwa, nie kąt) w daną stronę i zapis nowej wartości w EEPROM-ie, dzięki czemu kalibracji nie trzeba powtarzać. Ster wychyla się (offset jest włączany) tylko w czasie pracy silnika "do przodu", gdyż wtedy występuje opisane zjawisko (ruch do tyłu nie jest aż tak ważny). W praktyce działa to dobrze, acz nie idealnie, gdyż wraz z słabnięciem akumulatorów, spada również prędkość obrotowa silnika, więc "pierwotny" offset staje się zbyt silny i łódka znów zaczyna lekko skręcać. Niemniej nie jest problemem ponowne skalibrowanie, a robienie krzywej wychylenia w funkcji napięcia akumulatora nie jest warte ogromu czasu, który należałoby poświęcić na dodanie odpowiedniej funkcji w kodzie i jej kalibrację :D

    PS. Okazuje się, że jednak występują problemy w transmisji przy większych odległościach (krótkotrwałe przerwy w pracy silnika - prawdopodobnie błędnie odczytane wartości wskutek "zgubionyh" znaków zakończenia wiadomości, lub innych problemów). Być może wrócę jeszcze do ich rozwiązania. Nie jest to sprzęt ratujący życie i do zabawy aż wystarcza aż nadto, satysfakcja jest.

    
    #include <SoftwareSerial.h>
    #include <Adafruit_NeoPixel.h>
    #include <EEPROM.h>
    #include <PWMServo.h>//software PWM used because servo library had conflict with other used functions using PWM generated by the same internal clock
    PWMServo myservo;  // create servo object to control a servo
    PWMServo esc;  // create servo object to control an ESC (motor controller)
    SoftwareSerial mySerial(2, 3); //RX, TX
    Adafruit_NeoPixel
    strip = Adafruit_NeoPixel(3, 13, NEO_GRB + NEO_KHZ800); //NO of pixels, PIN
    byte af, ab, bf, bb;
    boolean light = 0; //LIGHT ON/OFF, default = OFF
    boolean change = false; // flag to mark change to use WS2812B communication once for light ON/OFF. Constant communication interrupts message receiving
    boolean lightadjustment = false; // flag to mark change to use WS2812B communication once for light level adjustment. Constant communication interrupts message receiving
    byte xy; //light brightness pre set setting
    byte brightness = 64; //brightness level (0-255), default = 64
    boolean boatmode;// if true, then boat mode
    byte modecount = 0; //no. of successful read sequences
    int  bootval;// voltage check at boot to detect whether it is a boat or not
    byte neutral = 86; //neutral position of servo
    byte neutralmove;// neutral position of servo during boat's forward movement
    byte servoangle; //servo's angle
    byte escangle;// mapped value to be sent as "servo" output to ESC
    int serwoPWM;// calculated value to steer servo by generating PWM
    unsigned long counter; //time from last message
    unsigned long nowtime; //time now. Used further to calculate time past since previous loop
    //unsigned long pinginterval; // for signalling range ping messages
    float voltageread; //voltage read at input
    float batvoltage; //calculated battery level (incl. calculated offset)
    byte mediumdischargecount = 0; //to avoid one - time battery voltage drops,alarm will be activated after few times threshold is reached
    byte deepdischargecount = 0; //to avoid one - time battery voltage drops,alarm will be activated after few times threshold is reached
    long mediumbatlevelalarmtime; //time of buzzer's activation
    boolean mediumbatlevelalarmactivation = false;
    boolean deepdischargealarmactivation = false;
    byte maxleftangle = 55;//MAX LEFT RUDDER DEFLECTION OF BOAT (IN RANGE OF 0-255, NOT DEGREE)
    byte maxrightangle = 45;//MAX RIGHT RUDDER DEFLECTION OF BOAT (IN RANGE OF 0-255, NOT DEGREE)
    byte newoffsetvalue; // new offset value to be saved in EEPROM
    boolean updateeepromflag = false; //if true, then offset value in EEPROM must be updated
    
    //int lowestbatvoltage; ////////////////////USED ONLY AT BOOT TO HAVE ANY VALUE FOR IF FUNCTION TO COMPARE TO
    //int lowestbatEEPROM;
    
    
    void setup()
    {
      //writeIntIntoEEPROM(17000);//EEPROM LOW RESET USED TO START A NEW RECORDING
      //PINS A6&A7 cannot be used as analog output in Arduino Nano
      pinMode(4, OUTPUT); //AT MODE SWITCH
      pinMode(5, OUTPUT);
      pinMode(6, OUTPUT);
      pinMode(10, OUTPUT);//SERVO
      pinMode(13, OUTPUT); //built in led + WS2812B
      pinMode(A1, OUTPUT); //GND for voltage sensing
      pinMode(A0, INPUT); //voltage check at boot to detect whether it is a boat or not
      pinMode(A2, INPUT); //battery voltage check
      pinMode(A5, OUTPUT); //low battery buzzer output
      digitalWrite(A1, LOW); //GND for voltage sensing
      digitalWrite(A5, LOW); //deactivate buzzer
      digitalWrite(4, LOW); //ENTER AT MODE
      delay(50);//necessary to wait after AT mode change
    
      Serial.begin(9600);
      mySerial.begin(9600);//adjust after adjusting communication speed using AT+BXXX
      mySerial.println("AT+FU3");
      delay(50);
      mySerial.println("AT+B9600");
      delay(50);
      mySerial.println("AT+RX"); //AT MODE REQUIRED TO BE ON (4 LOW)
      delay(100);
      digitalWrite(4, HIGH); //EXIT AT MODE
      delay(50);//necessary to wait after mode change\
      analogReference(INTERNAL); //1,1V
      modecheck(); //Function to detect whether hardware is a boat or not
    
      analogReference(INTERNAL); //1,1V internal refercence voltage for battery measurement
    
      if (boatmode == true)
      {
        myservo.attach(SERVO_PIN_B); //Library works only at PIN9 and 10 - rudder 10
        esc.attach(SERVO_PIN_A); //Library works only at PIN9 and 10 - motor controller 9
        //ESCinit(); //ARM ESC (MOTOR CONTROLLER). NOT USED WITH THIS ESC; OWN SEQUENCE USED
        servostart(); //Sequence for servo movement at boat's boot
        testbeep(); //Test buzzer
        //Serial.print("LOWEST VOLTAGE FROM EEPROM: ");
        //Serial.println(readIntFromEEPROM());
        //Serial.print("OFFSET READ FROM EEPROM: ");
        //Serial.println(readoffsetfromeeprom());// read rudder's offset value during forward movement to compensate for propeller's movement
        neutralmove = readoffsetfromeeprom(); //set new offset value as neutral while moving forward
        //Serial.print("NEUTRAL MOVEMENT OFFSET: ");
        //Serial.println(neutralmove);
      }
      else
      {
        strip.begin(); // INITIALIZE NeoPixel strip object
        strip.clear();
        strip.show();
        startsequence(); //LED BLINK at car's boot
      }
    
    } ///////////////////////////////////////////END OF SETUP
    
    
    void loop()
    {
    
      ///////////////////////LOST CONNECTION PROTECTION
      nowtime = millis();
      if ((nowtime - counter) > 1000) //1000 MS SIGNAL LOST TRIGGER
      {
        //Serial.println(nowtime-counter);
        //Serial.println("CONNECTION LOST !!!");
        if (boatmode == false) //IF IN CAR MODE
        {
          digitalWrite(11, LOW);
          digitalWrite(10, LOW);
          digitalWrite(6, LOW);
          digitalWrite(5, LOW);
        }
        else //IF IN BOAT MODE
        {
          digitalWrite(6, LOW);
          digitalWrite(5, LOW);
        }
      }
      ///////////////////////END OF LOST CONNECTION PROTECTION
    
      //////////////////////////100 MS SIGNAL PING TRIGGER
      //    if ((nowtime - pinginterval) > 50)
      //    {
      //      //Serial.println("PING INTERVAL");
      //      //Serial.println(nowtime-pinginterval);//Loop time
      //      pinginterval = millis();
      //      ();
      //    }
      ///////////////////////END OF SIGNAL PING TRIGGER
    
    
      ////////////////////////////////////// MESSAGE RECEIVING
      while (mySerial.available())
      {
        static String receivedMessage = ""; // Store the received message
        char receivedChar = mySerial.read();
    
        if (receivedMessage.length() == 0)
        {
          if (receivedChar == 'A' || receivedChar == 'B' || receivedChar == 'C' || receivedChar == 'D')
          {
            receivedMessage = receivedChar; // Start building the message with the received character
          }
          if (receivedChar == 'F')
          {
            light = !light;
            change = true; // FLAG FOR LIGHT STATE CHANGE
            //Serial.println("LIGHT ON/OFF !");
          }
          if (receivedChar == 'E')
          {
            lightadjustment = true; // FLAG FOR LIGHT ADJUSTMENT
            updateeepromflag = true; //Flag to mark that offset value in EEPROM must be updated
          }
        }//////////////END OF receivedMessage.length() == 0
    
        else
        {
          if (receivedChar == '!')
          {
            // Message received and ends with '!'
            //Serial.println(receivedMessage);
            // Serial.println("MESSAGE RECEIVED, CONNECTION OK!");
            counter = millis();
            processMessage(receivedMessage, boatmode, neutral, deepdischargealarmactivation);
            receivedMessage = ""; // Clear the received message for the next one
          }
          else
          {
            receivedMessage += receivedChar;
          }
        }
      }//END OF WHILE
      /////////////////////////////////// END OF MESSAGE RECEIVING
    
      ////////////////////////////////// LIGHT ON&OFF
      if (light == true && change == true) //turn ON the light to the pre set level
      {
        change = false; //function activated only once
        lighton(); // turn ON the light function
      }
    
      if (light == false && change == true) //turn OFF the light
      {
        change = false; //function activated only once
        lightoff(); // turn the light OFF function
      }
      ////////////////////////////////// END OF LIGHT ON&OFF
    
    
      ////////////////////////////////// LIGHT LEVEL & ADJUST
      if (lightadjustment == true)
      {
        if (xy == 0)
        {
          //Serial.println("MAX");
          strip.setBrightness(255);
          strip.show();
          xy = 1;
          lightadjustment = false;
        }
      }
    
      if (lightadjustment == true)
      {
        if (xy == 1)
        {
          //Serial.println("DEFAULT");
          strip.setBrightness(64);
          strip.show();
          xy = 0;
          lightadjustment = false;
        }
      }
      ////////////////////////////////// END OF LIGHT LEVEL &ADJUST
    
      ///////////////////////////////////SAVE NEW RUDDER'S OFFSET IN EEPROM
      if (updateeepromflag == true)
      {
    
        if (bf > 3)
        {
          newoffsetvalue = neutralmove - 1;
          updateoffsettoeeprom (newoffsetvalue); //save to EEPROM
        }
        if (bb > 3)
        {
          newoffsetvalue = neutralmove + 1;
          updateoffsettoeeprom (newoffsetvalue); //save to EEPROM
        }
        //Serial.print("OLD MOVE NEUTRAL OFFSET: ");
        //Serial.println(neutralmove);
        neutralmove = newoffsetvalue; // update also current setting
        updateeepromflag = false; //execute only once
        //Serial.print("NEW MOVE NEUTRAL OFFSET: ");
        //Serial.println(neutralmove);
    
      }
      ////////////////////////////////// END OF SAVE NEW RUDDER'S OFFSET IN EEPROM
    
    
      /////////////////////////////////// VOLTAGE READ AND BUZZER ACTIVATION
      if (boatmode == true)
      {
        voltageread = analogRead(A2);
        //Serial.print("BAT VOLTAGE READ: ");
        //Serial.println(voltageread);
        batvoltage = 10.88 * (long)voltageread * 1100 / 1024; //Convert voltageread to long to correctly handle multiplication (without int overflow). Voltage appplied through 1/10 divider. 1,1 is internal reference voltage. 10* because of reststor divider, .88 for calibration
        //Serial.print("CALCULATED VOLTAGE: ");
        //Serial.println(batvoltage);
      }
    
      //batvoltage=1.3;// BUZZER & LOW BATTERY PWM TEST
    
    
      if (batvoltage <= 6.8)
      {
        if (mediumbatlevelalarmactivation == false) //if medium battery level alarm has not been activated yet
        {
          mediumdischargecount = mediumdischargecount + 1;
          //Serial.print("MEDIUM BAT VOLTAGE COUNT:");
          //Serial.println(mediumdischargecount);
        }
      }
    
      if (batvoltage <= 6.0)
      {
        if (deepdischargealarmactivation == false) ////if battery deep discharge alarm has not been activated yet
        {
          deepdischargecount = deepdischargecount + 1;
          //Serial.print("DEEP BAT VOLTAGE COUNT:");
          //Serial.println(deepdischargecount);
        }
      }
    
      if (mediumdischargecount == 10) //medium battery level alarm activation
      {
        if (mediumbatlevelalarmactivation == false) //activate medium battery alarm only once
        {
          mediumbatlevelalarmtime = millis();
          Serial.println("MEDIUM BATTERY LEVEL ALARM !!!");
          digitalWrite(A5, HIGH); //activate buzzer
          mediumbatlevelalarmactivation = true; //medium battery alarm will not be activated again
        }
      }
    
      if (((millis() - mediumbatlevelalarmtime) > 10000 && (millis() - mediumbatlevelalarmtime) < 14000) ) //if alarm has been activated for more than 10 seconds but only once
      {
        digitalWrite(A5, LOW); //turn off the buzzer
      }
    
      if (deepdischargecount == 10) //low battery level alarm activation
      {
        digitalWrite(A5, HIGH); //activate buzzer
        if (deepdischargealarmactivation == false)
        {
          Serial.println("LOW BATTERY LEVEL ALARM !!!"); //send message only once
        }
        deepdischargealarmactivation = true;
        digitalWrite(A5, HIGH); //permanently activate the buzzer
      }
    
      /////////////////////////////////// END OF VOLTAGE READ AND BUZZER ACTIVATION
    
      //  /////////////////////////////////// VOLTAGE READ AND EEPROM READ&WRITE
      //    if (boatmode == true)
      //    {
      //      voltageread = analogRead(A2);
      //      Serial.print("ANALOG VOLTAGE: ");
      //      Serial.println(voltageread);
      //      batvoltage = 10.88 * (long)voltageread * 1100 / 1024; //Convert voltageread to long to correctly handle multiplication (without int overflow). Voltage appplied through 1/10 divider. 0.88 after 10 for calibration
      //      Serial.print("READ   VOLTAGE: ");
      //      Serial.println(batvoltage);
      //
      //      if (batvoltage > 3000) //to prevent from reading when battery is not connected (analog pin is grounded by 1k resistor)
      //      {
      //        if (batvoltage < lowestbatEEPROM) //if current voltage is lower than the lowest recorded in EEPROM
      //        {
      //          lowestbatvoltage = batvoltage;
      //          writeIntIntoEEPROM(lowestbatvoltage);
      //          Serial.print("!!!!!! NEW LOWEST VOLTAGE RECORDED: ");
      //          Serial.println(lowestbatvoltage);
      //        }
      //      }
      //
      //      Serial.print("LOWEST VOLTAGE: ");
      //      Serial.print(lowestbatvoltage);
      //      Serial.println(" (SINCE BOOT)");
      //      lowestbatEEPROM = readIntFromEEPROM(); //read lowest battery value stored in EEPROM
      //      Serial.print("LO BAT EEPROM : ");
      //      Serial.println(lowestbatEEPROM);
      //    }/////////////////////////////////// END OF VOLTAGE READ AND EEPROM READ&WRITE
    
    
    }////////////////////////////////////END OF LOOP
    
    
    
    //////////////////////////////////////////FUNCTIONS/////////////////////////////////////////////////
    
    ///////////////////////////////////////////PROCESS MESSAGE
    void processMessage(String receivedMessage, boolean boatmode, byte bootval, boolean deepdischargealarmactivation)
    {
      //Serial.println(receivedMessage); //////////////////////////////// RECEIVED MESSAGE
      if (receivedMessage.charAt(0) == 'A')
      {
        af = receivedMessage.substring(1).toInt();
      }
    
      if (receivedMessage.charAt(0) == 'B')
      {
        ab = receivedMessage.substring(1).toInt();
      }
    
      if (receivedMessage.charAt(0) == 'C')
      {
        bf = receivedMessage.substring(1).toInt();
      }
    
      if (receivedMessage.charAt(0) == 'D')
      {
        bb = receivedMessage.substring(1).toInt();
      }
    
    
      if (boatmode == false) //////////////////////PWM GENERATION IF IN CAR MODE
      {
        if (af > 3) //A FORWARD PWM&LOW
        {
          analogWrite(11, af);
          digitalWrite(10, LOW);
        }
    
        if (ab > 3) //A BACKWARD PWM&LOW
        {
          digitalWrite(11, LOW);
          analogWrite(10, ab);
        }
    
        if (bf > 3) //B FORWARD PWM&LOW
        {
          analogWrite(6, bf);
          digitalWrite(5, LOW);
        }
    
        if (bb > 3) //B BACKWARD ENGINE PWM&LOW
        {
          digitalWrite(6, LOW);
          analogWrite(5, bb);
        }
    
        if (bf <= 3 && bb <= 3) //FOR ZEROING AS PROGRAM DO NOT ZERO BY DEFAULT(DO NOT INTERPRETE "0" VALUE)
        {
          digitalWrite(6, LOW);
          digitalWrite(5, LOW);
        }
    
        if (af <= 3 && ab <= 3) //FOR ZEROING AS PROGRAM DO NOT ZERO BY DEFAULT (DO NOT INTERPRETE "0" VALUE)
        {
          digitalWrite(10, LOW);
          digitalWrite(11, LOW);
        }
      } ////////////////////////END OF CAR MODE PWM GENERATION
    
      else//////////////////////PWM GENERATION IF IN BOAT MODE
      {
        ////////////////////////////////////////////////////////////IF ESC CONTROLLER USED
        if (af > 3) //A FORWARD ESC
        {
          if (deepdischargealarmactivation == false) //if deep battery discharege alarm is not activated
          {
            escangle = map(af, 3, 255, 90, 180);
            esc.write(escangle);
            myservo.write(neutralmove); //set rudder to offset during forward movement
            //Serial.println("OFFSET MOVE ACTIVATED!!");
          }
          else //if deep battery discharege alarm is activated
          {
            escangle = map(af, 3, 255, 90, 120); //max speed reduced to 1/3
            esc.write(escangle);
            myservo.write(neutralmove); //set rudder to offset during forward movement
            //Serial.println("OFFSET MOVE ACTIVATED!!");
          }
        }
    
        if (ab > 3) //A BACKWARD ESC
        {
          if (deepdischargealarmactivation == false) //if deep battery discharege alarm is not activated
          {
            escangle = map(ab, 3, 255, 90, 10); //cannot be "0", issue with ESC (too low PWM signal?)
            esc.write(escangle);
          }
          else //if deep battery discharege alarm is activated
          {
            escangle = map(ab, 3, 255, 90, 70); //max speed reduced to 1/3
            esc.write(escangle);
          }
        }
        ////////////////////////////////////////////////////////////END OF IF ESC CONTROLLER USED
    
        ////////////////////////////////////////////////////////////IF PWM H-BRIDGE CONTROLLER USED
        //    if (af > 3) //A FORWARD PWM&LOW
        //    {
        //      if (deepdischargealarmactivation == false) //if deep battery discharege alarm is not activated
        //      {
        //        analogWrite(5, af);
        //        digitalWrite(6, LOW);
        //        myservo.write(neutralmove); //set rudder to offset during forward movement
        //      }
        //      else //if deep battery discharege alarm is activated
        //      {
        //        analogWrite(5, (af / 3)); //speed reduced by 33%
        //        digitalWrite(6, LOW);
        //        myservo.write(neutralmove); //set rudder to offset during forward movement
        //      }
        //    }
        //
        //    if (ab > 3) //A BACKWARD PWM&LOW
        //    {
        //      if (deepdischargealarmactivation == false) //if deep battery discharege alarm is not activated
        //      {
        //        digitalWrite(5, LOW);
        //        analogWrite(6, ab);
        //      }
        //      else //if deep battery discharege alarm is activated
        //      {
        //        digitalWrite(5, LOW);
        //        analogWrite(6, (ab / 3)); //speed reduced by 33%
        //      }
        //    }
        ////////////////////////////////////////////////////////////END OF IF PWM H-BRIDGE CONTROLLER USED
    
        if (bf > 3) //B FORWARD PWM&LOW -->here SERVO RIGHT
        {
          if (bf < 240) // MORE DELICATE RUDDER MOVEMENT FOR SMALLER JOYSTICK'S ANGLE
          {
            servoangle = map(bf, 3, 255, neutral, (neutral - 35));
            myservo.write(servoangle);
          }
    
          else      //LARGER JOYSTICK'S ANGLE - NORMAL RESPONSE
          {
            servoangle = map(bf, 3, 255, neutral, (neutral - maxrightangle));
            myservo.write(servoangle);
          }
        }
    
        if (bb > 3) //B BACKWARD ENGINE PWM&LOW --> here SERVO LEFT
        {
          if (bb < 240) // MORE DELICATE RUDDER MOVEMENT FOR SMALLER JOYSTICK'S ANGLE
          {
            servoangle = map(bb, 3, 255, neutral, (neutral + 35));
            myservo.write(servoangle);
          }
          else    //LARGER JOYSTICK'S ANGLE - NORMAL RESPONSE
          {
            servoangle = map(bb, 3, 255, neutral, (neutral + maxleftangle));
            myservo.write(servoangle);
          }
        }
        //    {
        //      servoangle = map(bf, 3, 255, neutral, (neutral - maxrightangle));
        //      myservo.write(servoangle);
        //    }
        //
        //    if (bb > 3) //B BACKWARD ENGINE PWM&LOW --> here SERVO
        //    {
        //      servoangle = map(bb, 3, 255, neutral, (neutral + maxleftangle));
        //      myservo.write(servoangle);
        //    }
    
        if (af <= 3 && ab <= 3) //FOR ZEROING AS PROGRAM DO NOT ZERO BY DEFAULT (DO NOT INTERPRETE "0" VALUE)
        {
          esc.write(90);//if ESC used
          //digitalWrite(5, LOW); //IF PWM H-BRIDGE CONTROLLER USED
          //digitalWrite(6, LOW); //IF PWM H-BRIDGE CONTROLLER USED
        }
    
        if (bf <= 3 && bb <= 3 ) //FOR NEUTRAL POSITION  AS PROGRAM DO NOT ZERO BY DEFAULT(DO NOT INTERPRETE "0" VALUE)
        {
          if (af <= 3) //WITHOUT OVERWRITING OFFSET IF IN MOVE
          {
            myservo.write(neutral);
          }
        }
      } ////////////////////////END OF BOAT MODE PWM GENERATION
    
      //    FOR DEBUG ONLY. USING INTERRUPTS RECEIVED SIGNAL & OPERATION
      //    Serial.print("AF: ");
      //    Serial.println(af);
      //    Serial.print("AB: ");
      //    Serial.println(ab);
      //    Serial.print("BF: ");
      //    Serial.println(bf);
      //    Serial.print("BB: ");
      //    Serial.println(bb);
      //    FOR DEBUG ONLY. USING INTERRUPTS RECEIVED SIGNAL & OPERATION
    } ///////////////////////////////////////////////////////////////////////////////////////////////Process Message End
    
    void lighton() ////////////////////////////LED ON IN CAR MODE
    {
      strip.setBrightness(brightness);
      strip.setPixelColor(0, strip.Color(255, 255, 255));
      strip.setPixelColor(1, strip.Color(255, 255, 255));
      strip.setPixelColor(2, strip.Color(255, 0, 0));
      strip.show();
    }
    
    void lightoff() //////////////////////////// LED OFF IN CAR MODE
    {
      strip.setBrightness(0);
      strip.show();
    }
    
    void startsequence() ////////////////////////////LED BLINK AT START IN CAR MODE
    {
      digitalWrite(13, HIGH); //BUILT IN LED ON
      strip.setPixelColor(0, strip.Color(255, 255, 255));
      strip.setPixelColor(1, strip.Color(255, 255, 255));
      strip.setPixelColor(2, strip.Color(255, 0, 0));
      strip.show();
      delay(50);
      digitalWrite(13, LOW); //BUILT IN LED OFF
      strip.setBrightness(0);
      strip.show();
      delay(50);
      digitalWrite(13, HIGH); //BUILT IN LED ON
      strip.setBrightness(255);
      strip.setPixelColor(0, strip.Color(255, 255, 255));
      strip.setPixelColor(1, strip.Color(255, 255, 255));
      strip.setPixelColor(2, strip.Color(255, 0, 0));
      strip.show();
      delay(50);
      digitalWrite(13, LOW); //BUILT IN LED OFF
      strip.setBrightness(0);
      strip.show();
    } ////////////////////////////END OF LED BLINK AT START IN CAR MODE
    
    void servostart() ////////////////////////////SEQUENCE OF SERVO MOVEMENTS AT START
    {
      myservo.write(neutral + maxleftangle);                // sets the servo position according to the scaled value - left
      delay(200);
      myservo.write(neutral - maxrightangle);                // sets the servo position according to the scaled value - right
      delay(200);
      myservo.write(neutral); // sets the servo position according to the scaled value
      delay(100);
    }
    
    void testbeep()
    {
      digitalWrite(A5, HIGH);
      delay(50);
      digitalWrite(A5, LOW);
      delay(50);
      digitalWrite(A5, HIGH);
      delay(50);
      digitalWrite(A5, LOW);
      delay(50);
      digitalWrite(A5, HIGH);
      delay(50);
      digitalWrite(A5, LOW);
    }
    
    void shortbeep()
    {
      digitalWrite(A5, HIGH);
      delay(25);
      digitalWrite(A5, LOW);
    }
    
    boolean modecheck() ////////////////////////////voltage check at boot to detect whether it is a boat or not
    {
      for (byte i = 0; i < 5; i++)
      {
        bootval = analogRead(A0);
        //Serial.println(bootval);//for calibration
        if (bootval > 600 && bootval < 810) //A0 value at USB approx. 770, at boat different -- FORUSB to be calibrated for each Arduino independently using Serial.println(bootval), for battery to be calculated manually or readand store in EEPROM;
        {
          modecount = modecount + 1;
        }
        delay(100);
      }
    
      if (modecount == 5)
      {
        boatmode = true;
        Serial.println("BOAT MODE !");
        return boatmode;
      }
      else
      {
        Serial.println("CAR MODE !");
      }
    }/////////////////////////////////////// END OF MODE CHECK
    
    void ESCinit()//////////////////////////ESC INITIATION (ARM) SEQUENCE
    {
      byte pos = 0;
      esc.write(0);
    
      for (pos = 0; pos < 181; pos += 1)
      {
        esc.write(pos);
        delay(10);
        //Serial.println(pos);
      }
      shortbeep();
      delay(200);
    
      for (pos = 180; pos < 181; pos -= 1)
      {
        esc.write(pos);
        delay(10);
        //Serial.println(pos);
      }
      shortbeep();
      delay(200);
      for (pos = 0; pos < 91; pos += 1)
      {
        esc.write(pos);
        delay(10);
        //Serial.println(pos);
      }
      shortbeep();
      delay(200);
    }
    
    void updateoffsettoeeprom (byte newoffsetvalue)
    {
      EEPROM.update(3, newoffsetvalue); //address, variable
    }
    
    byte readoffsetfromeeprom()
    {
      return neutralmove = EEPROM.read(3);
    }
    
    //void writeIntIntoEEPROM(int lowestbatvoltage)
    //{
    //  EEPROM.update(0, lowestbatvoltage >> 8); //address, variable. Shift by 8n bite, meaning saving only firt 8 bits from the digit will be in the first (out of two) bytes
    //  EEPROM.update(0 + 1, lowestbatvoltage & 0xFF); //For the second byte, we make an AND operation with 0xFF, which is the hexadecimal representation of 255 (you could write “& 255” instead of “& 0xFF”). This way, we only get the 8 bits on the right.
    //}
    
    //int readIntFromEEPROM()
    //{
    //  return lowestbatEEPROM = (EEPROM.read(0) << 8) + EEPROM.read(0 + 1);
    //}
    
    //void sendping()
    //{
    //  mySerial.print("XXXXX");
    //  Serial.println("!"); //////////////////////////////// PING SENT
    //}
    

Podsumowanie tematu

✨ Użytkownik prezentuje uniwersalną platformę do obsługi zdalnie sterowanych modeli, zaczynając od podwozia czołgu zakupionego w chińskim sklepie. W dyskusji poruszane są różne aspekty zdalnego sterowania, w tym zasięg transmisji, zastosowanie modułów HC-12, LoRa oraz nRF24L01. Użytkownicy dzielą się doświadczeniami z prędkościami modeli, takimi jak łódka Feilun FT009 (30 km/h) i Revell Aqua Crawler (20 km/h). Wprowadzono również zmiany w oprogramowaniu, takie jak obsługa regulatorów ESC oraz optymalizacja kodu, co zwiększa uniwersalność projektu. Dodatkowo, omawiane są problemy z opóźnieniami w transmisji i stabilnością sygnału.
Wygenerowane przez model językowy.
REKLAMA