logo elektroda
logo elektroda
X
logo elektroda

The second life of RC models, i.e. remote control by Frog_Qmak

Frog_Qmak 4935 17
ADVERTISEMENT
Treść została przetłumaczona polish » english Zobacz oryginalną wersję tematu
  • Remote-controlled off-road car with antenna, standing on a wooden floor.

    Hello

    I would like to present and share a universal platform for operating remotely controlled models.
    I haven`t posted anything on the forum for many years - it doesn`t mean that nothing was happening, but it wasn`t always worth showing. ;)


    It all started with the fact that I wanted to do something that would give me fun and be an inspiration to "fiddle" with cables and Arduino. So I ordered a tank chassis from a Chinese online store (sinoning.com) and that`s how the adventure began... after the tank, a mini-car appeared (also from the same store), and then, bought for 1/3 of the value of new vehicles, a full-fledged car with 4x4 drive and a remote-controlled fast boat, bought on Allegro as "damaged". The damage to the car consisted of water getting inside and seizing the engine (the car is theoretically waterproof and has positive buoyancy and can also "move" on water), which was revived (by force and then turning by hand), while the boat was theoretically damaged. control, but I didn`t check what happened because from the very beginning I wanted to install my own...

    A few words about the control itself:
    Remote consists of 2 joysticks (Arduino modules), Arduino Nano, Li-Ion battery, BMS system, 5V step-up converter and radio (HC-12 module). No rocket science. I had a huge problem diagnosing the problem that the BMS did not want to charge the battery - the diode was blinking very quickly, as if there was no contact somewhere, or the module was being energized. In desperation, I soldered it directly to the battery (previously through the switch) and this solved the problem. I don`t know why the switch caused such problems - perhaps the contacts provided some electrical resistance and the module was excited, e.g. by the radio or the converter. The remote control has two modes (the mode is changed by long pressing the left joystick): car/boat mode (default - left joystick forward/back, right joystick right/left) and tank mode (both front/rear joysticks for differential control). Holding the left joystick for a shorter time changes the lamp mode (bright/medium/off) in the big car.

    Receivers they also consist of Arduino Nano, HC-12, as well as (cars, tank) BMS and an H-bridge for controlling the engines (in a boat, the engine is operated by a servo). The smallest car is powered by one LI-ION cell and a STEP-UP converter, hence the large capacitor so that voltage drops do not reset the Arduino. The remaining models run on two 18650s (the large car also has a capacitor on the power supply, but it is not needed).

    Additional information
    The large car has lights based on WS2812B (two white at the front, one red at the rear).

    Boat steeringis more complicated because the "ordinary PWM code" cannot be used to operate the servo, as it is done in a different way. All receivers have an identical code; At startup, Arduino checks whether there is appropriate voltage on one of the pins (supplied from the 3.3V output) and if so, it activates the "boat mode". Other models do not have this pin bridged - this way the program is universal and there is no need to add another "mode" in the remote control.

    The boat has protection against low battery voltage - it is measured by a voltage divider and has two activation thresholds. When the first one is reached, an acoustic signal is emitted for a few seconds (5V buzzer controlled by the BS170 transistor). When the second one is reached, the acoustic signal turns on permanently and the engine power is reduced (PWM max 50%). The BMS used in the factory cells does not cut off the power supply even in the event of deep discharge, which makes sense because it is better to kill the batteries than to get the boat stuck 10 meters from the shore...
    The servo is controlled using a software library. This is because the default servo library had a conflict with the library responsible for controlling the WS2812 (probably a conflict due to the use of the same timer).

    The program also has commented sections related to testing the battery in the boat - before I discovered that the BMS did not cut it off, I wrote a script that saved the lowest measured supply voltage in EEPROM and after connecting the Arduino to the computer, sent it via Serial. I wanted to know to what level the cells could be discharged to properly set the protection thresholds, but it turned out to be unnecessary.

    The remote control and the large toy car have connectors for charging the batteries; there are chargers for a small car and a boat.

    Generally, I discovered that the "spiral" antenna in the transmitter works poorly, but a regular wire of appropriate length works well. The antennas in the remote control and cars are mounted in goldpin sockets so that they can be easily removed for transport. The boat has a default spiral antenna. HC-12 radios are very cool, the theoretical range (at low bitrate, direct visibility) is up to one kilometer. I am attaching two documents that will be useful for their operation - they contain a description of the functions and configuration.

    Calibration is needed to adapt the code to a given joystick (neutral point) and servo (also). The values are described in the comments.

    I don`t have any diagrams, but I think you can easily figure out what and how based on the descriptions and photos. I know that (especially in the remote control) there is a spider`s web of cables, but I don`t waste my time designing boards, I enjoy electronics and writing programs.
    The code is heavily commented (in English), so understanding the programs should not be a problem. The range in the selected configuration (radio bitrate, antenna) is at least 200m (maybe more, I haven`t checked, at such a distance it is difficult to control the model).
    If anyone is interested, it is worth following this topic, if there is an update of the program(s), I will add it.

    Below are the photos and code.
    Remote control with two joysticks on a black box for remote-controlled devices.
    Interior of a remote control with electronic components and connected wires.
    Chassis of a remote-controlled car model with Arduino electronics and wires.
    Close-up of the interior of an electronic project's enclosure with components.
    Remote-controlled off-road car with antenna, standing on a wooden floor.
    Electronic setup with Arduino modules inside a remote-controlled vehicle.
    Close-up of dual Li-Ion battery cells mounted in a black case, surrounded by wires and a fuse.
    Remote-controlled boat with visible interior and electronic components.
    Close-up of electronics inside an orange remote-controlled boat.

    TRANSMITTER
    // 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


    RECEIVER
    #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
    //}

    Cool? Ranking DIY
    Do you have a problem with Arduino? Ask question. Visit our forum Arduino.
    About Author
    Frog_Qmak
    Level 25  
    Offline 
    Frog_Qmak wrote 1319 posts with rating 293, helped 7 times. Live in city Kraków. Been with us since 2007 year.
  • ADVERTISEMENT
  • #2 20840369
    gulson
    System Administrator
    Thank you very much for your presentations. There haven`t been any projects related to RC models for a long time. Write to me parcel locker and I will send you a small gift!
  • #3 20840472
    Karol966
    Level 31  
    One of the stores wrote:
    "Transmission range: from 1 m to 1000 m (less than 1 meter, it may not work properly)", seriously, as much as 1000 meters can be achieved with this?
  • ADVERTISEMENT
  • #4 20840483
    Frog_Qmak
    Level 25  
    I haven`t tried it - perhaps it is possible with the lowest bitrate and using directional antennas. I was positively surprised when it turned out that it covered the 200 m (I didn`t try any further) with the "spiral" antenna on the receiver, partially hidden in a plastic boat and additionally covered by me (I was walking with my back to the transmitter, carrying it in my hands).
  • #5 20841216
    Jawi_P
    Level 36  
    I haven`t heard about HC-12 modules, maybe I`ll try it someday. It`s nice that the RC project has appeared.
  • ADVERTISEMENT
  • #6 20841316
    ArturAVS
    Moderator
    @Frog_Qmak Żabko_croaking :D , have you considered using LoRa modules? Or e.g. nRF24L01? I had some stuff accumulated and I would build something.
  • ADVERTISEMENT
  • #7 20841809
    Frog_Qmak
    Level 25  
    I found a tutorial for this radio on the Internet :) I tried NRF once, but I couldn`t get them to work, so they ended up in the box, and then, after a few years, I came across HC :) I haven`t tried LoRa and I don`t know how expensive they are :)
  • #8 20842248
    bb84
    Level 21  
    Something like this would be useful to control a submarine at a depth of 2-3 m.
  • #9 20842364
    Krzysztof Kamienski
    Level 43  
    @Frog_Qmak, maybe it`s not stupid, but with the editing style you ruined my appetite for breakfast. Sorry.
  • #10 20842596
    Frog_Qmak
    Level 25  
    @Krzysztof Kamienski, I know it`s not great inside, ;) but excessive pampering does not give me satisfaction, I prefer to develop the program or new models and have fun with it, rather than force myself and "burn out".
  • #11 20843105
    OPservator
    Level 38  
    What is the maximum speed?
  • #12 20843857
    Frog_Qmak
    Level 25  
    A boat (Feilun FT009) supposedly 30 km/h, a large car (Revell Aqua Crawler) supposedly 20 km/h :) I will only test the boat outdoors, but it has a specific engine with water cooling, it consumes 8-12 A.
  • #13 20850945
    mpier
    Level 29  
    Hello
    I don`t know the modules and I haven`t looked into the code too much, but the sending method used in the program may have (has?) a significant drawback - the high overhead of the si4463 protocol. In addition, there is the delay of stm8s itself, which does not know how much data you are sending and probably waits a certain time after the last byte before sending it to the transmitter (hence probably the delay (5 ms) after transmission.
    If you also receive one channel at a time and update the timer each time from the last received packet, there is a small chance that you will manage to send only temporarily less important packets, and the needed ones will not arrive and the receiver will not notice it (e.g. those about braking will not arrive). ).

    I think that by sending everything together and selecting the appropriate data length (there will probably be room for the checksum and maybe the packet number), you can increase the control speed, error detection efficiency and at the same time reduce bandwidth consumption (the average transmission time will drop quite a bit). With the package number, you can determine the signal quality and react, e.g. in the case of a weak battery.

    For smaller cars, this hc12 module should work without Arduino if you reprogram it.

    Regards
  • #14 20850977
    Frog_Qmak
    Level 25  
    Hello, thank you for your valuable attention.
    In the case of a large model (which could, for example, be a threat due to its weight) or other applications where reliability is important, this would need to be paid attention to.
    In my particular case, the remote control sends data non-stop, so if the joystick is in the neutral position, the receiver will receive this packet (even if not the first time).
    Previously, I struggled with the problem you described (message length - all positions sent in one message), but I optimized the code and now the messages are much shorter.
    Good idea with this packet numbering to assess signal quality - I must remember it!
  • #15 21046407
    Frog_Qmak
    Level 25  
    Hello,

    I am adding updated (improved) versions of the software for the transmitter and receiver.


    Main changes:
    1. I added support for model engine controllers (ESC), as an alternative to H-bridges. I had a lot of problems with burning H-bridges (the engine draws 10...13A anyway, so it generates a lot of overvoltages*), I lost several months testing various solutions, was placed on the Redox Ultra 45 regulator. It is important that the regulator must be "armed" at the start, simulating the neutral position of the stick at the start and the appropriate sequence of movements (max down, max up, neutral). I had another "Chinese" 20 A on the way (lots of them on Allegro), I tested many combinations, none of them worked. Redox started immediately.
    2. I added a second "!" character in the transmitter. at the end of the message. Often a single one "got lost" (I suppose it could have been a matter of further optimization of the receiver code, or some conflicts in its timers), causing operational instability ("vibrations" of the servo and micro-interruptions of the engine). The second sign does not harm anything and the problems disappeared immediately.
    3. Various minor optimizations to the code and its readability.

    *I burned many bridges this way, as well as the stabilizer built into Arduino. Transiles and additional capacitors did not help. For those interested, I`ll send it to you LINK to the thread.

    TRANSMITTER
    
    //! 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
    


    RECEIVER
    
    #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.
    Level 35  
    It`s hard to call it optimization until you eliminate a lot of delay from your code, especially the receiver. Do this exercise and send each time an incremented counter of sent frames in a frame on the transmitter side and increase the counter of received frames on the receiver side, then compare both counters on the receiver side and see how much difference you have :)

    Regards
  • #17 21048100
    Frog_Qmak
    Level 25  
    Thanks for your comment, it is true that the code could be further optimized, but it would require significant modifications. So far it works fine, the range is more than sufficient, so there is no need to dig it out. An insert with ESC support may be useful to someone, I have read a lot about how to approach it, this way the project gains universality.
  • #18 21120072
    Frog_Qmak
    Level 25  
    Welcome,

    I am adding another revision of the receiver code. In addition to minor improvements, there are two significant changes.

    1. as the boat is VERY manoeuvrable, i.e. at high speed and rudder swing, it almost stops and turns in place, I have added a rudder swing limiter, i.e. at a certain joystick swing, the corresponding servo movement is weakened, while to maintain high manoeuvrability at near end position, the servo reacts again in a violent manner.

    (2) As the propeller, rotating at a tremendous speed, causes the boat to pivot along its axis and thus twist, I added an 'offset' with the possibility of calibrating it. This works by swinging the joystick on the remote control that corresponds to the direction in which the rudder should swing, and then pressing and holding a button on the other joystick (like to change the lights on a car). This changes the "offset" by 1 (servo position, not angle) in that direction and stores the new value in the EEPROM, so calibration does not need to be repeated. The rudder only swings (the offset is switched on) when the motor is running 'forward', as this is when the phenomenon described occurs (movement backwards is not as important). In practice this works well, albeit not perfectly, because as the batteries weaken, the engine speed also decreases, so the 'original' offset becomes too strong and the boat starts to turn slightly again. Nevertheless, it is not a problem to recalibrate, and doing a camber curve as a function of battery voltage is not worth the huge amount of time it would take to add a suitable function in the code and calibrate it :D .

    PS. It turns out, however, that there are problems in transmission at longer distances (short interruptions of the engine - probably misread values due to "lost" message completion characters, or other problems). I may yet return to resolve these. This is not a life-saving piece of equipment and for playing around until it is sufficient, the satisfaction is there.

    
    #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
    //}
    
    .

Topic summary

The discussion revolves around the development and enhancement of a universal platform for remote-controlled (RC) models, initiated by the user Frog_Qmak. The project began with a tank chassis and expanded to include a mini-car, a 4x4 vehicle, and a remote-controlled boat, all sourced from online stores. Participants discuss various technical aspects, including transmission range, the use of different communication modules like HC-12, LoRa, and nRF24L01, and the challenges faced with existing protocols and hardware. The author shares insights on optimizing code for better performance, implementing electronic speed controllers (ESC), and improving maneuverability in high-speed models. The conversation highlights the importance of reliability in remote control systems and the ongoing development of software to enhance functionality.
Summary generated by the language model.
ADVERTISEMENT