Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!

Remote Control with ESP8266 D1 Mini

andre

New member
Joined
Sep 28, 2024
Messages
14
I have two ESP8266 D1 Minis that I would like to use as remote control and receiver for a small robot car. I can probably figure out the inputs and outputs on my own, but the P2P wifi connection and the messaging, packets and sending data and telemetry is beyond my skills.

Can someone help me make a basic program that showcases sending data from one D1 Mini to another? Like - two potentiometers on one D1 controlling the PWM brightness of LED's on the second D1.
 
Hey! Sounds like a fun project with the robot car. I can definitely help you get started.

First, I'd like to note that the D1 Mini only has ONE analog input (A0), so using two potentiometers really isn't possible. One option could be to use a rotary encoder instead of the second potentiometer. With a rotary encoder, you can also click to select different parameters (like speed, direction, or LED brightness) and then turn to adjust the value. This would help save on input pins but it's a bit clunky.
D1-Mini-PINS.jpg
If you're set on using two potentiometers, you could still use a D1 mini as the RX and a different ESP microcontroller for the TX.

Now, for the communication part. I mostly work in the Arduino IDE, I can guide you through a basic peer-to-peer (P2P) setup using the ESP-NOW protocol to send data between the two D1 Minis. It's lightweight and good for direct device-to-device communication without needing a router. This would be a more responsive way to go unless you have other reasons for using a WiFi network.

Alternatively, if you’re interested in scaling this project or using it for home automation purposes, you might want to explore MQTT. It’s a messaging protocol that’s great for handling multiple devices and could be more of a turn-key solution, especially if you're planning to control things like lighting and such.

If you don't mind sharing the details of what you want to accomplish, I can help tailor something specific. Let me know what direction you want to go, and I can help you with some code examples and wiring diagrams!
 
Awesome - good to know about the different protocols. ESP-NOW sounds like a good plan for this project, which is primarily just proof of concept & a learning experience.

A single analog input does limit some other plans I had for the D1's, but I'm sure there are other electronics that are better suited for those (weather station with LoRa comms).

My current project is to control this guy - 4 DC motors driven by a PWM controlled driver (L298N).

The receiver to driver outputs would be 1 PWM to set the speed for all motors and 8 digital to set the DC direction.

The remote controller could be made to work with digital inputs only for now... such as a 4x4 button array or a resistor-based 5 button joystick (from a RunCam FPV camera!) which I think is a really clever single analog input solution.

For now, I don't care so much about having many analog inputs, just understanding the ESPNOW protocol and sending and reading messages :)

1727632357017.jpeg
 
ESP-NOW is a great fit for sending commands between your D1 Minis without the need for a Wi-Fi network. I recommend walking through this test before going all the way just to get used to everything first :)

I’ve written two simple sketches, one for the transmitter (TX) and one for the receiver (RX). In this example, the transmitter (TX) uses four buttons to control the brightness of an LED on the receiver (RX) side. The buttons send different states (1 through 4), and the receiver adjusts the LED brightness based on the received value.

How it works:

  1. TX Code:
    • Four buttons are connected to digital pins on the TX D1 Mini.
    • When a button is pressed, the TX reads the button state and sends a corresponding number to the RX via ESP-NOW.
    • The message is sent only when the button state changes, so you avoid sending data unnecessarily.
  2. RX Code:
    • The RX receives the data and changes the brightness of an LED based on the value it gets from the TX.
    • The brightness is controlled using the PWM feature of the D1 Mini, and the brightness levels are low, medium, high, and max.
Let's set up two D1 Mini boards to communicate wirelessly using the ESP-NOW. One D1 Mini will be the transmitter (TX) with 4 buttons, and the other will be the receiver (RX) that controls an LED’s brightness (simulating motor control).

Hardware Needed:​

  • 2x D1 Mini (ESP8266)
  • 1x LED (with a current-limiting resistor, ~220-1K ohms)
  • 4x Push buttons
  • Jumper wires and breadboard

Build the CircuitS:

I went ahead and built this test myself to make sure it works. (NOTE: don't use my MAC address, you need to use the one from YOUR RX)
IMG_8396.jpg

Transmitter (TX) – 4 Buttons

  1. Connect One Side of each of the 4 buttons to the following digital pins: D1, D2, D5, D6.
  2. The other side of each button should be connected to GND.
  3. When pressed, each button will send a unique message.

Receiver (RX) – LED

  1. Connect an LED to pin D4 (GPIO2) of the RX D1 Mini.
  2. Place a 220 to 1K ohm resistor in series with the LED to prevent overcurrent.
  3. One end of the resistor goes to D4, the other end goes to the LED anode(+) while the LED cathode (-) goes to GND.

Setting up the MAC Address

  1. Find the MAC Address of the Receiver D1 Mini:
    • Upload the RX code to one of your D1 Minis first
    • Open the Serial Monitor (make sure the baud rate is set to 115200).
    • The RX code will print the MAC address of the receiver to the Serial Monitor as soon as the D1 Mini boots up. You’ll see something like:
      Code:
      Receiver MAC Address: XX:XX:XX:XX:XX:XX
    • This is the MAC address you need to use in the TX code so the two devices can communicate directly.
  2. Replace the MAC Address in the TX Code:
    • Copy the MAC address printed in the Serial Monitor.
    • In the TX code, replace the placeholder MAC address in this line:
      Code:
      uint8_t receiverMAC[] = {0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX};  // Replace with the actual RX MAC address
    • You’ll need to format the MAC address as hex values. For example, if your MAC address is 2C:3A:E8:27:87:94, you would write it like this:
      Code:
      uint8_t receiverMAC[] = {0x2C, 0x3A, 0xE8, 0x27, 0x87, 0x94};
TX Code:

C++:
/*  ESP-NOW Transmitter Code for Button Inputs
Created by HackMakeMod

This code demonstrates using ESP-NOW to send button states
from one ESP8266 (D1 Mini) to another.
It reads four buttons on the TX D1 Mini and sends a value (1-4)  to the RX D1 Mini,
which adjusts an LED’s brightness.
Ideal for remote control systems or P2P communication projects.
For more tutorials and DIY projects, visit HackMakeMod.com */

#include <ESP8266WiFi.h>
#include <espnow.h>

uint8_t receiverMAC[] = {0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX}; // Replace with your receiver's MAC address

int button1 = D1; // GPIO5
int button2 = D2; // GPIO4
int button3 = D5; // GPIO14
int button4 = D6; // GPIO12
int buttonState = 0;
int lastButtonState = -1; // Initialize to an invalid state

void setup() {
  Serial.begin(115200);

  // Initialize WiFi
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  // Initialize ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Set ESP-NOW role
  esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);

  // Register the peer (receiver)
  if (esp_now_add_peer(receiverMAC, ESP_NOW_ROLE_SLAVE, 0, NULL, 0) != 0) {
    Serial.println("Failed to add peer");
    return;
  }

  // Set button pins as inputs with pull-up resistors
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);
  pinMode(button4, INPUT_PULLUP);

  Serial.println("ESP-NOW Transmitter Initialized");
}

void loop() {
  // Check which button is pressed
  if (digitalRead(button1) == LOW) {
    buttonState = 1; // Button 1 pressed
  } else if (digitalRead(button2) == LOW) {
    buttonState = 2; // Button 2 pressed
  } else if (digitalRead(button3) == LOW) {
    buttonState = 3; // Button 3 pressed
  } else if (digitalRead(button4) == LOW) {
    buttonState = 4; // Button 4 pressed
  } else {
    buttonState = 0; // No button pressed
  }

  // Send the button state to the receiver only if it has changed
  if (buttonState != lastButtonState) {
    uint8_t result = esp_now_send(receiverMAC, (uint8_t *)&buttonState, sizeof(buttonState));

    // Check if the message was sent successfully
    if (result == 0) {
      Serial.print("Sent button state: ");
      Serial.println(buttonState);
    } else {
      Serial.print("Error sending data: ");
      Serial.println(result);
    }

    lastButtonState = buttonState;
  }

  delay(50); // Adjust this delay based on your requirements
}

RX Code:
C++:
/*  ESP-NOW Receiver Code for Controlling LED Brightness
Created by HackMakeMod

This code listens for button state data sent via ESP-NOW from
a transmitter (TX) ESP8266 (D1 Mini). It adjusts the brightness
of an LED based on the received button values (1-4).

This is useful for remote control systems or similar P2P
communication setups where you want simple, responsive control
over outputs.

For more DIY projects and tutorials, visit HackMakeMod.com*/

#include <ESP8266WiFi.h>
#include <espnow.h>

int ledPin = D4; // GPIO2
int receivedValue = 0; // Variable to store the received button value

// Callback function to handle data received over ESP-NOW
void onDataRecv(uint8_t *mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&receivedValue, incomingData, sizeof(receivedValue));

  if (receivedValue != 0) {
    Serial.print("Received button: ");
    Serial.println(receivedValue);
      // Print the MAC address to Serial Monitor (so you can use it in the TX code)
    Serial.print("RX MAC Address: ");
    Serial.println(WiFi.macAddress());

    // Control LED brightness based on received value
    if (receivedValue == 1) {
      analogWrite(ledPin, 10); // Low brightness
    } else if (receivedValue == 2) {
      analogWrite(ledPin, 60); // Medium brightness
    } else if (receivedValue == 3) {
      analogWrite(ledPin, 150); // High brightness
    } else if (receivedValue == 4) {
      analogWrite(ledPin, 255); // Max brightness
    }
  } else {
    // Turn off the LED when no button is pressed
    analogWrite(ledPin, 0);
  }
}

void setup() {
  Serial.begin(115200);

  // Initialize WiFi and disconnect to ensure a clean start
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();  // Ensure Wi-Fi is fully disconnected

  // Print the MAC address to Serial Monitor (so you can use it in the TX code)
  Serial.print("Receiver MAC Address: ");
  Serial.println(WiFi.macAddress());

  // Initialize ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Set ESP-NOW role
  esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);

  // Register the receive callback function
  esp_now_register_recv_cb(onDataRecv);

  // Set LED pin as output
  pinMode(ledPin, OUTPUT);
  analogWrite(ledPin, 0); // Initially turn off LED

  Serial.println("ESP-NOW Receiver Initialized");
}

void loop() {
  // Nothing to do here, data is handled in onDataRecv
}

This should be enough to get you started. If you need more help, just let me know and I'll be happy to get you to the next level!

Otherwise, please share your progress here!

Thanks,
Chad
 
Last edited:
I got it working! I did some minor modifications though. I moved this part to the end of setup, as it wasn't readable, but the Initialized message was readable. Putting this after the receiver Initialized message made the MAC address readable.
Code:
Serial.print("Receiver MAC Address: ");
Serial.println(WiFi.macAddress());

Also since I'm lazy, I used the built in LED instead of setting up one. Worked fine :)

Now - how to send multiple messages at the same time? Need to decipher this code

Code:
uint8_t result = esp_now_send(receiverMAC, (uint8_t *)&buttonState, sizeof(buttonState));
 
Actually, that IS sending multiple messages... MAC address, buttonState, and whatever sizeof is.
 
I made an esp32 version for my Traxxas summit RC car. It uses two steering servos, one front differential lock servo and one for the back as well as one for high/low gear. The code disconnects the aux servos after use so as to reduce the current draw from the BEC. Here is the raw code, mostly as inspiration

Arduino Code:
#include <esp_now.h>
#include <WiFi.h>
#include <ESP32Servo.h>

const unsigned long failsafeTimeout = 3000; // Failsafe timeout in milliseconds (3 seconds)
const unsigned long servoAttachDuration = 1500; // Servo attach duration in milliseconds (3 seconds)

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
  int steering;
  int throttle;
  int gear;
  int lockServo1;
  int lockServo2;
} struct_message;

// Create a struct_message called myData
struct struct_message myData;

// Create Servo objects
Servo steeringServo;
Servo steeringServo2;
Servo throttleServo;
Servo gearServo;
Servo lockServo1;
Servo lockServo2;

unsigned long lastReceiveTime = 0; // Variable to store the time of the last received package
unsigned long servoChangeTime = 0; // Variable to store the time of the last servo change
bool gearAttached = false; // Flag to track if the gear servo is attached
bool lockServo1Attached = false; // Flag to track if lockServo1 is attached
bool lockServo2Attached = false; // Flag to track if lockServo2 is attached
int lastGearPosition = 1500; // Variable to store the last gear position
int lastLockServo1Position = 1500; // Variable to store the last lockServo1 position
int lastLockServo2Position = 1500; // Variable to store the last lockServo2 position

// callback function that will be executed when data is received
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) {
  memcpy(&myData, incomingData, sizeof(myData));

  // Update the last receive time
  lastReceiveTime = millis();

  // Write received values directly to servo positions
  steeringServo.writeMicroseconds(myData.steering);
  steeringServo2.writeMicroseconds(myData.steering);
  throttleServo.writeMicroseconds(myData.throttle);

  // Check if the gear value has changed
  if (myData.gear != lastGearPosition) {
    // Update the last gear change time
    servoChangeTime = millis();

    // Attach the gear servo if it's not already attached
    if (!gearAttached) {
      gearServo.attach(27); // Replace '27' with the actual pin number for the gear servo
      gearAttached = true;
    }

    // Move the gear servo to the new position
    gearServo.writeMicroseconds(myData.gear);

    // Update the last gear position
    lastGearPosition = myData.gear;
  }

  // Check if lockServo1 value has changed
  if (myData.lockServo1 != lastLockServo1Position) {
    // Update the last lockServo1 change time
    servoChangeTime = millis();

    // Attach lockServo1 if it's not already attached
    if (!lockServo1Attached) {
      lockServo1.attach(26); // Replace '26' with the actual pin number for LockServo1
      lockServo1Attached = true;
    }

    // Move lockServo1 to the new position
    lockServo1.writeMicroseconds(myData.lockServo1);

    // Update the last lockServo1 position
    lastLockServo1Position = myData.lockServo1;
  }

  // Check if lockServo2 value has changed
  if (myData.lockServo2 != lastLockServo2Position) {
    // Update the last lockServo2 change time
    servoChangeTime = millis();

    // Attach lockServo2 if it's not already attached
    if (!lockServo2Attached) {
      lockServo2.attach(25); // Replace '25' with the actual pin number for LockServo2
      lockServo2Attached = true;
    }

    // Move lockServo2 to the new position
    lockServo2.writeMicroseconds(myData.lockServo2);

    // Update the last lockServo2 position
    lastLockServo2Position = myData.lockServo2;
  }
}

void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);
  Serial.println("Booting");
  Serial.println("Waiting for TX");

  // Attach ESP32Servo objects to corresponding pins
  steeringServo.attach(12);  // Replace '12' with the actual pin number for the steering servo
  throttleServo.attach(14);  // Replace '14' with the actual pin number for the throttle servo
  steeringServo2.attach(33); // Replace '33' with the actual pin number for the second steering servo

  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packet info
  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {
  // Check for throttle failsafe
  if (millis() - lastReceiveTime > failsafeTimeout) {
    // No valid package received for 3 seconds, set throttle to 1500
    throttleServo.writeMicroseconds(1500);

    // Uncomment the following line if you need to print failsafe activation occasionally
    Serial.println("Throttle failsafe activated!");
  }

  // Check if the gear servo is attached and if it's time to detach it
  if (gearAttached && (millis() - servoChangeTime > servoAttachDuration)) {
    gearServo.detach();
    gearAttached = false;

    // Uncomment the following line for debugging
    Serial.println("Gear servo detached");
  }

  // Check if lockServo1 is attached and if it's time to detach it
  if (lockServo1Attached && (millis() - servoChangeTime > servoAttachDuration)) {
    lockServo1.detach();
    lockServo1Attached = false;

    // Uncomment the following line for debugging
    Serial.println("LockServo1 detached");
  }

  // Check if lockServo2 is attached and if it's time to detach it
  if (lockServo2Attached && (millis() - servoChangeTime > servoAttachDuration)) {
    lockServo2.detach();
    lockServo2Attached = false;

    // Uncomment the following line for debugging
    Serial.println("LockServo2 detached");
  }

  // Do other tasks if needed
}
 
This is fun! Do you have the code for the transmitting end as well?
 
Silly of me to forget! Here you go:

Code:
#include <esp_now.h>
#include <WiFi.h>

// Receiver MAC Address
uint8_t broadcastAddress[] = {0x30, 0xAE, 0xA4, 0x96, 0x5D, 0xEC};

// Tunable
int numReadings = 5; // Number of readings to average
int minSteering = 460; // What the pots/analog inputs are actually giving out as max and min
int maxSteering = 3200;
int minThrottle = 300;
int maxThrottle = 3200;
int throttleDeadband = 120; // Set the deadband range for the throttle here
int steeringDeadband = 50; // Set the deadband range for the steering here
int midPointRawSteering = 1950; // Set your desired midpoint for Steering here
int midPointRawThrottle = 1200;
int midPointMappedThrottle = 1500;
int SteeringTrimInfluence_min = -200;
int SteeringTrimInfluence_max = 200;
int steeringTrimDeadband = 20; // Set your desired deadband for changes in steeringTrim
unsigned long debounceDelay = 50; // Adjust the debounce delay as needed

// Endpoint adjustments // Flip values to reverse channel
int HighGearValue = 2000;
int LowGearValue = 1000;
int LockServo1_min = 1000;
int LockServo1_max = 2000;
int LockServo2_min = 2000;
int LockServo2_max = 1000;
int ThrottleEPA_max = 2000;
int ThrottleEPA_min = 1000;
int SteeringEPA_max = 2000;
int SteeringEPA_min = 1000;

// Non-tunable
int lastSteeringTrim = 0;
int steeringReadings[5] = {0, 0, 0, 0, 0};
int throttleReadings[5] = {0, 0, 0, 0, 0};
int filteredSteering = 0;
int filteredThrottle = 0;
int filteredSteeringTrim = 0;
bool firstSuccessfulPackage = false;

// Debounce variables
unsigned long lastHighGearTime = 0;
bool highGearState = HIGH;
unsigned long lastLowGearTime = 0;
bool lowGearState = HIGH;
unsigned long lastLockFTime = 0;
bool lockFState = HIGH;
unsigned long lastLockBTime = 0;
bool lockBState = HIGH;

// Pin assignments
const int analogInputPinSteering = 32;
const int analogInputPinThrottle = 33;
const int analogInputPinSteeringTrim = 35;
const int analogInputPinThrottleTrim = 34;
const int digitalInputPinHighGear = 18;
const int digitalInputPinLowGear = 19;
const int digitalInputPinLockF = 16;
const int digitalInputPinLockB = 17;
const int beeper = 27;

unsigned long lastSerialPrintTime = 0;
unsigned long serialPrintInterval = 500; // Update interval for Serial print in milliseconds

int totalPackagesSent = 0;
int successfulPackages = 0;

// Structure to send data
typedef struct struct_message {
  int steering;
  int throttle;
  int gear;
  int lockServo1;
  int lockServo2;
} struct_message;

struct_message myData;

esp_now_peer_info_t peerInfo;

// Callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  totalPackagesSent++; // Update how many packages were sent
  if (status == ESP_NOW_SEND_SUCCESS) {
    successfulPackages++; // Update how many were successful
  }
}

void setup() {
  Serial.begin(115200);

  // Set up pin modes
  pinMode(digitalInputPinHighGear, INPUT_PULLUP);
  pinMode(digitalInputPinLowGear, INPUT_PULLUP);
  pinMode(digitalInputPinLockF, INPUT_PULLUP);
  pinMode(digitalInputPinLockB, INPUT_PULLUP);
  pinMode(beeper, OUTPUT);

  // Set up WiFi and ESPNow
  WiFi.mode(WIFI_STA);
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  esp_now_register_send_cb(OnDataSent);

  // Set up peer information
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("Failed to add peer");
    return;
  }

  // Play startup sounds on the beeper
  for (int i = 0; i < 10; i++) {
    digitalWrite(beeper, HIGH);
    delay(10);
    digitalWrite(beeper, LOW);
    delay(10);
  }
  delay(200);
  for (int i = 0; i < 10; i++) {
    digitalWrite(beeper, HIGH);
    delay(10);
    digitalWrite(beeper, LOW);
    delay(10);
  }
}

void loop() {
  // Read raw values from analog input pins
  int rawSteering = analogRead(analogInputPinSteering);
  int rawThrottle = analogRead(analogInputPinThrottle);
  int rawSteeringTrim = analogRead(analogInputPinSteeringTrim);
  int rawThrottleTrim = analogRead(analogInputPinThrottleTrim);

  // Apply averaging filter to raw Steering
  for (int i = 0; i < numReadings - 1; ++i) {
    steeringReadings[i] = steeringReadings[i + 1];
  }
  steeringReadings[numReadings - 1] = rawSteering;
  filteredSteering = 0;
  for (int i = 0; i < numReadings; ++i) {
    filteredSteering += steeringReadings[i];
  }
  filteredSteering /= numReadings;

  // Apply averaging filter to raw Throttle
  for (int i = 0; i < numReadings - 1; ++i) {
    throttleReadings[i] = throttleReadings[i + 1];
  }
  throttleReadings[numReadings - 1] = rawThrottle;
  filteredThrottle = 0;
  for (int i = 0; i < numReadings; ++i) {
    filteredThrottle += throttleReadings[i];
  }
  filteredThrottle /= numReadings;

  // Apply averaging filter to raw SteeringTrim
  filteredSteeringTrim = rawSteeringTrim;

  // Apply deadband for Steering
  if (filteredSteering >= midPointRawSteering - steeringDeadband && filteredSteering <= midPointRawSteering + steeringDeadband) {
    filteredSteering = midPointRawSteering;
  }

  // Apply deadband for SteeringTrim
  if (abs(filteredSteeringTrim - lastSteeringTrim) < steeringTrimDeadband) {
    filteredSteeringTrim = lastSteeringTrim;
  }

  lastSteeringTrim = filteredSteeringTrim;

  // Map the SteeringTrim to an influence range
  int mappedSteeringTrim = map(filteredSteeringTrim, 0, 4095, SteeringTrimInfluence_min, SteeringTrimInfluence_max);

  // Constrain values within defined limits
  filteredSteering = constrain(filteredSteering, minSteering, maxSteering);
  filteredThrottle = constrain(filteredThrottle, minThrottle, maxThrottle);

  // Map the Steering and Throttle values to their defined ranges
  int mappedSteering = map(filteredSteering, minSteering, maxSteering, SteeringEPA_max, SteeringEPA_min);
  mappedSteering += mappedSteeringTrim;
  mappedSteering = constrain(mappedSteering, SteeringEPA_min, SteeringEPA_max);

  // Apply deadband for Throttle
  if (filteredThrottle >= midPointRawThrottle - throttleDeadband && filteredThrottle <= midPointRawThrottle + throttleDeadband) {
    filteredThrottle = midPointRawThrottle;
  }

  // Map the Throttle value to its defined range
  int mappedThrottle = map(filteredThrottle, minThrottle, maxThrottle, ThrottleEPA_max, ThrottleEPA_min);
  if (filteredThrottle > midPointRawThrottle) {
    mappedThrottle = map(filteredThrottle, midPointRawThrottle, maxThrottle, midPointMappedThrottle, ThrottleEPA_min);
  } else {
    mappedThrottle = map(filteredThrottle, minThrottle, midPointRawThrottle, ThrottleEPA_max, midPointMappedThrottle);
  }

  // Read digital input values for gear shifting and servo locking
  int digitalValueHighGear = digitalRead(digitalInputPinHighGear);
  if (digitalValueHighGear != highGearState && millis() - lastHighGearTime > debounceDelay) {
    lastHighGearTime = millis();
    highGearState = digitalValueHighGear;
  }

  int digitalValueLowGear = digitalRead(digitalInputPinLowGear);
  if (digitalValueLowGear != lowGearState && millis() - lastLowGearTime > debounceDelay) {
    lastLowGearTime = millis();
    lowGearState = digitalValueLowGear;
  }

  int digitalValueLockF = digitalRead(digitalInputPinLockF);
  if (digitalValueLockF != lockFState && millis() - lastLockFTime > debounceDelay) {
    lastLockFTime = millis();
    lockFState = digitalValueLockF;
  }

  int digitalValueLockB = digitalRead(digitalInputPinLockB);
  if (digitalValueLockB != lockBState && millis() - lastLockBTime > debounceDelay) {
    lastLockBTime = millis();
    lockBState = digitalValueLockB;
  }

  // Determine the gear based on high/low gear states
  int Gear = (highGearState == HIGH && lowGearState == LOW) ? HighGearValue : LowGearValue;

  // Determine servo positions based on locking states
  int LockServo1 = 0;
  int LockServo2 = 0;
  if (lockFState == LOW && lockBState == HIGH) {
    LockServo1 = LockServo1_min;
    LockServo2 = LockServo2_min;
  } else if (lockFState == HIGH && lockBState == HIGH) {
    LockServo1 = LockServo1_max;
    LockServo2 = LockServo2_min;
  } else if (lockFState == HIGH && lockBState == LOW) {
    LockServo1 = LockServo1_max;
    LockServo2 = LockServo2_max;
  }

  // Populate the data structure
  myData.steering = static_cast<int>(mappedSteering);
  myData.throttle = static_cast<int>(mappedThrottle);
  myData.gear = Gear;
  myData.lockServo1 = LockServo1;
  myData.lockServo2 = LockServo2;

  // Send data using ESP-NOW
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *)&myData, sizeof(myData));

  // Print information to Serial Monitor at specified intervals
  if (millis() - lastSerialPrintTime >= serialPrintInterval) {
    lastSerialPrintTime = millis();

    // Print successful data packages
    Serial.print("Successful data packages: ");
    Serial.print(successfulPackages);
    Serial.print("/");
    Serial.println(totalPackagesSent);

    // Beeper warning code
    if (successfulPackages > 0) {
      firstSuccessfulPackage = true; // Set the flag after the first successful package
    }

    // Check if more than half of packages are lost after the first successful package
    if (firstSuccessfulPackage == true) {
      if (successfulPackages < totalPackagesSent / 2) {
        digitalWrite(beeper, !digitalRead(beeper)); // Toggle the output state
      } else {
        digitalWrite(beeper, LOW);
      }
    }

    // Reset counts after printing
    totalPackagesSent = 0;
    successfulPackages = 0;

    /*// Print various sensor and control values
    Serial.print("Steering (Raw): ");
    Serial.print(filteredSteering);
    Serial.print(" | Throttle (Raw): ");
    Serial.print(filteredThrottle);
    Serial.print(" | Steering: ");
    Serial.print(mappedSteering);
    Serial.print(" | Throttle: ");
    Serial.print(mappedThrottle);
    Serial.print(" | SteeringTrim (Raw): ");
    Serial.print(filteredSteeringTrim);
    Serial.print(" | MappedSteeringTrim: ");
    Serial.print(mappedSteeringTrim);
    Serial.print(" | HighGear: ");
    Serial.print(highGearState);
    Serial.print(" | LowGear: ");
    Serial.print(lowGearState);
    Serial.print(" | LockF: ");
    Serial.print(lockFState);
    Serial.print(" | LockB: ");
    Serial.print(lockBState);
    Serial.print(" | Gear: ");
    Serial.print(Gear);
    Serial.print(" | LockServo1: ");
    Serial.print(LockServo1);
    Serial.print(" | LockServo2: ");
    Serial.println(LockServo2);*/
  }
}
 
Back
Top