I have one Arduino nano as the receiver and one ESP32 as transceiver, both with a nRF24L01+. The motor in question is connected to a L298 mini H-Bridge. But I don't know why it makes a weird high-pitched sound when the transceiver joystick is centered.
I know the issue is on the receiver code, because the previous version didn't make this weird noise. But I unfortunately lost it.
Transceiver code:
#include <Arduino.h>
#include <SPI.h>
#include <U8g2lib.h>
#include "nRF24L01.h"
#include "RF24.h"
#define LCDWidth u8g2.getDisplayWidth()
#define ALIGN_CENTER(t) ((LCDWidth - (u8g2.getUTF8Width(t))) / 2)
#define ALIGN_RIGHT(t) (LCDWidth - u8g2.getUTF8Width(t))
#define ALIGN_LEFT 0
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0); // All Boards without Reset of the Display
// 'Max', 20x10px
const unsigned char maxbat[] PROGMEM = {
0xfc, 0xff, 0x0f, 0x04, 0x00, 0x08, 0xf7, 0xde, 0x0b, 0xf1, 0xde, 0x0b, 0xf1, 0xde, 0x0b, 0xf1,
0xde, 0x0b, 0xf1, 0xde, 0x0b, 0xf7, 0xde, 0x0b, 0x04, 0x00, 0x08, 0xfc, 0xff, 0x0f
};
// 'Medium', 20x10px
const unsigned char medbat[] PROGMEM = {
0xfc, 0xff, 0x0f, 0x04, 0x00, 0x08, 0x07, 0xde, 0x0b, 0x01, 0xde, 0x0b, 0x01, 0xde, 0x0b, 0x01,
0xde, 0x0b, 0x01, 0xde, 0x0b, 0x07, 0xde, 0x0b, 0x04, 0x00, 0x08, 0xfc, 0xff, 0x0f
};
// 'Low', 20x10px
const unsigned char lowbat[] PROGMEM = {
0xfc, 0xff, 0x0f, 0x04, 0x00, 0x08, 0x07, 0xc0, 0x0b, 0x01, 0xc0, 0x0b, 0x01, 0xc0, 0x0b, 0x01,
0xc0, 0x0b, 0x01, 0xc0, 0x0b, 0x07, 0xc0, 0x0b, 0x04, 0x00, 0x08, 0xfc, 0xff, 0x0f
};
// 'ConfigIcon', 13x13px
const unsigned char configIcon[] PROGMEM = {
0x00, 0x00, 0x4e, 0x04, 0x4a, 0x04, 0x4e, 0x04, 0x44, 0x0e, 0x44, 0x0a, 0x44, 0x0e, 0x44, 0x04,
0xe4, 0x04, 0xa4, 0x04, 0xe4, 0x04, 0x44, 0x04, 0x00, 0x00
};
// 'JogosIcon', 10x10px
const unsigned char JogosIcon[] PROGMEM = {
0x02, 0x01, 0xfd, 0x02, 0x01, 0x02, 0xcd, 0x02, 0x45, 0x02, 0x01, 0x02, 0x79, 0x02, 0x85, 0x02,
0x85, 0x02, 0x02, 0x01
};
// 'VoltarIcon', 10x10px
const unsigned char VoltarIcon[] PROGMEM = {
0x30, 0x00, 0x18, 0x00, 0x0c, 0x00, 0xfe, 0x00, 0x0c, 0x01, 0x18, 0x02, 0x30, 0x02, 0x00, 0x01,
0xfc, 0x00, 0x00, 0x00
};
// 'JoysticksIcon', 10x10px
const unsigned char JoysticksIcon[] PROGMEM = {
0x30, 0x00, 0x78, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0x78, 0x00, 0x30, 0x00, 0x30, 0x00, 0x78, 0x00,
0xfe, 0x01, 0xff, 0x03
};
// 'PowerSavingIcon', 10x10px
const unsigned char PowerSavingIcon[] PROGMEM = {
0x30, 0x00, 0xfc, 0x00, 0x84, 0x00, 0x94, 0x00, 0xa4, 0x00, 0x94, 0x00, 0xa4, 0x00, 0x94, 0x00,
0x84, 0x00, 0xfc, 0x00
};
// 'ControlesIcon', 10x10px
const unsigned char ControlesIcon[] PROGMEM = {
0x78, 0x00, 0x86, 0x01, 0x32, 0x01, 0x49, 0x02, 0x49, 0x02, 0x79, 0x02, 0x49, 0x02, 0x4a, 0x01,
0x86, 0x01, 0x78, 0x00
};
const uint8_t *bitmaps[] = { VoltarIcon, ControlesIcon, PowerSavingIcon, JogosIcon, JoysticksIcon, VoltarIcon, VoltarIcon };
// ============================================================ //
RF24 radio(4, 5);
int MidpointEsqX = 119;
int MidpointEsqY = 119;
int MidpointDirX = 119;
int MidpointDirY = 119;
uint8_t Tolerance = 5;
int joyAX = 0; // Joystick Esquerdo X (0-255) (Midpoint: 119)
int joyAY = 0; // Joystick Esquerdo Y (0-255) (Midpoint: 119)
int joyBX = 0; // Joystick Direito X (0-255) (Midpoint: 119)
int joyBY = 0; // Joystick Direito Y (0-255) (Midpoint: 119)
struct MeuControleRemoto {
boolean BotEsqCim = 0;
boolean BotEsqEsq = 0;
boolean BotEsqDir = 0;
boolean BotEsqBai = 0;
boolean BotDirCim = 0;
boolean BotDirEsq = 0;
boolean BotDirDir = 0;
boolean BotDirBai = 0;
boolean Mts1 = 0;
boolean Mts2 = 0;
boolean Mts3 = 0;
boolean Mts4 = 0;
byte Pot1 = 0;
byte Pot2 = 0;
byte Pot3 = 0;
byte Pot4 = 0;
byte JoyDireitaX = 127;
byte JoyDireitaY = 127;
byte JoyEsquerdaX = 127;
byte JoyEsquerdaY = 127;
byte rcBattery = 50; // 3.7v -> 5v (dividir por 10)
};
MeuControleRemoto ControleRemoto;
byte joyAXp = 27;
byte joyAYp = 26;
byte joyBXp = 25;
byte joyBYp = 33;
// #define joyAXp = 0;
// #define joyAYp = 0;
// #define joyBXp = 0;
// #define joyBYp = 0;
#define BotEsqCimPin = 0;
#define BotEsqEsqPin = 0;
#define BotEsqDirPin = 0;
#define BotEsqBaiPin = 0;
#define BotDirCimPin = 0;
#define BotDirEsqPin = 0;
#define BotDirDirPin = 0;
#define BotDirBaiPin = 0;
#define Mts1Pin = 0;
#define Mts2Pin = 0;
#define Mts3Pin = 0;
#define Mts4Pin = 0;
#define Pot1Pin = 0;
#define Pot2Pin = 0;
#define Pot3Pin = 0;
#define Pot4Pin = 0;
const int numReadings = 10; // Número de leituras para calcular a média
const int numJoysticks = 4; // Número de entradas de joystick
// Pinos dos joysticks
const int joystickPins[numJoysticks] = {joyAXp, joyAYp, joyBXp, joyBYp};
struct Joystick {
int pin;
int readings[numReadings];
int readIndex;
long total;
int count;
};
Joystick joysticks[numJoysticks];
int getSmoothedJoystickValue(Joystick &joystick) {
joystick.total -= joystick.readings[joystick.readIndex];
int newValue = analogRead(joystick.pin);
joystick.readings[joystick.readIndex] = newValue;
joystick.total += newValue;
joystick.readIndex = (joystick.readIndex + 1) % numReadings;
if (joystick.count < numReadings) {
joystick.count++;
}
return joystick.total / joystick.count;
}
const unsigned char *batLevel;
const unsigned char *RCbatLevel;
char *rcName = "TESTE";
int canal = 123;
bool botaoPressionado = false;
uint8_t lastRcBattery = ControleRemoto.rcBattery;
uint8_t remoteBattery = 45;
uint8_t lastRemoteBattery = 45; // ficticia
unsigned long startMillis; // Millis para os loops em geral
unsigned long currentMillis; // Millis para os loops em geral
const int period = 5000; //the value is a number of milliseconds
bool menuAtivo = false;
int selected; // Para os menus
bool rodou = 0; // Para o display saber se ja atualizou a tela
void setup(void) {
Serial.begin(115200);
u8g2.begin();
startMillis = millis(); //initial start time
pinMode(14, INPUT_PULLUP);
pinMode(13, INPUT_PULLUP);
pinMode(12, INPUT_PULLUP);
for (int i = 0; i < numJoysticks; i++) {
joysticks[i] = {joystickPins[i], {0}, 0, 0, 0};
pinMode(joysticks[i].pin, INPUT);
}
if (radio.isChipConnected())
Serial.println("\n\nTransmitter NF24 connected to SPI");
else Serial.println("\n\nNF24 is NOT connected to SPI");
radio.begin(); // Inicializando o MÓDULO RF24l01 para comunicação.
radio.setAutoAck(false); // Desativando pacotes ACK (Pacote de Confirmação de Recebimento de Mensagem)
radio.setChannel(100); // Configurando Módulo para operar no canal número 1 (você pode escolher um canal de 0 a 127) (Canal Padrão é o 76)
radio.setDataRate(RF24_250KBPS); // Configurando Módulo para operar em uma taxa de 250kbps (Menor taxa de dados possível para um maior alcance do rádio)
radio.setPALevel(RF24_PA_HIGH); // Configurando Módulo para transmitir em potência máxima
radio.powerUp(); // Ativando Módulo, caso entre em estado de baixo consumo.
radio.openWritingPipe(0xE8E8F0F0E1LL); // Abrindo o meio de comunicação entre transmissor e receptor e configurando endereço de comunicação (0xE8E8F0F0E1LL)
radio.stopListening(); // Interrompendo mecanismos de recepção "escuta" do Módulo
}
// int x = 0;
void loop()
{
if (menuAtivo) {
menu();
return;
}
joyAX = map(getSmoothedJoystickValue(joysticks[0]), 0, 4095, 0, 255);
joyAY = map(getSmoothedJoystickValue(joysticks[1]), 0, 4095, 0, 255);
joyBX = map(getSmoothedJoystickValue(joysticks[2]), 0, 4095, 0, 255);
joyBY = map(getSmoothedJoystickValue(joysticks[3]), 0, 4095, 0, 255);
if ((joyAX != ControleRemoto.JoyEsquerdaX) || (joyAY != ControleRemoto.JoyEsquerdaY) ||
(joyBX != ControleRemoto.JoyDireitaX) || (joyBY != ControleRemoto.JoyDireitaY)) {
if ((joyAX > (MidpointEsqX + Tolerance)) || ((joyAX < (MidpointEsqX - Tolerance)))) {
ControleRemoto.JoyEsquerdaX = joyAX;
}
if ((joyAY > (MidpointEsqY + Tolerance)) || ((joyAY < (MidpointEsqY - Tolerance)))) {
ControleRemoto.JoyEsquerdaY = joyAY;
}
if ((joyBX > (MidpointDirX + Tolerance)) || ((joyBX < (MidpointDirX - Tolerance)))) {
ControleRemoto.JoyDireitaX = joyBX;
}
if ((joyBY > (MidpointDirY + Tolerance)) || ((joyBY < (MidpointDirY - Tolerance)))) {
ControleRemoto.JoyDireitaY = joyBY;
}
/*
Serial.print("joyAX: ");
Serial.print(joyAX);
Serial.print(" joyAY: ");
Serial.print(joyAY);
Serial.print(" | ");
Serial.print("joyBX: ");
Serial.print(joyBX);
Serial.print(" joyBY: ");
Serial.println(joyBY);
*/
}
if ((ControleRemoto.rcBattery != lastRcBattery || remoteBattery != lastRemoteBattery)/* && (rodou == 0)*/)
{
rodou = 1;
Serial.println("Atualizou");
u8g2.clearBuffer(); // clear the internal memory
bateria();
u8g2.setFont(u8g2_font_amstrad_cpc_extended_8u);
u8g2.setCursor(1, 13);
u8g2.print(F("CANAL: "));
u8g2.setCursor(55, 13);
u8g2.print(canal);
u8g2.setFont(u8g2_font_t0_11_mr);
u8g2.setCursor(ALIGN_CENTER(rcName), 30);
u8g2.print(rcName);
// Ícones
u8g2.drawXBMP(107, 3, 20, 10, batLevel);
u8g2.drawXBMP(107, 52, 20, 10, RCbatLevel);
u8g2.drawXBMP(1, 50, 13, 13, configIcon);
u8g2.drawHLine(0, 15, 128);
u8g2.drawHLine(0, 47, 128);
u8g2.sendBuffer(); // transfer internal memory to the display
//u8g2.setPowerSave(1);
//delay(5000);
//u8g2.setPowerSave(0);
//x += 1;
}
/*
if (x == 5) {
menuAtivo = true;
}
*/
for (int i = 0; i < 5; i++) {
radio.write(&ControleRemoto, sizeof(ControleRemoto));
// Serial.println(ControleRemoto.JoyDireitaX);
}
}
void Controles() {}
void PowerSaving() {}
void Jogos() {}
void Debug() {}
// =========MENU========= //
typedef void (*FuncPtr)();
void menu()
{
const char *options[5] = {
" Voltar",
" Controles",
" Power Saving",
" Jogos",
" Debug"
};
FuncPtr funcoesMenu[5] = {
loop,
Controles,
PowerSaving,
Jogos,
Debug
};
const int menuLength = sizeof(options) / sizeof(options[0]);
const unsigned long period = 20; // Intervalo em milissegundos
int start = 0;
selected = 0; // Começa no item "Voltar"
bool last12State = HIGH; // Invertido por causa do INPUT_PULLUP
bool last13State = HIGH;
bool last14State = HIGH;
while (menuAtivo) {
if (digitalRead(14) != last14State) {
if (digitalRead(14) == LOW) {
last14State = LOW;
botaoPressionado = true;
selected += 1;
if (selected >= menuLength) {
selected = 0; // Volta para o primeiro item
start = 0;
}
if (selected >= start + 3) {
start += 1; // Ajuste o início para rolar para baixo
}
}
delay(100);
}
last14State = digitalRead(14);
// Atualiza o estado anterior do botão
if (digitalRead(12) == LOW) {
botaoPressionado = true;
if (selected == 0) {
selected = menuLength - 1; // Volta para o último item
start = menuLength - 3; // Ajuste o início para mostrar os últimos itens
} else {
selected -= 1;
}
if (selected < start) {
start -= 1; // Ajuste o início para rolar para cima
}
}
if (digitalRead(13) == LOW) {
botaoPressionado = true;
if (selected == 0) {
menuAtivo = false;
return;
}
funcoesMenu[selected](); // Chame a função correspondente ao item selecionado
}
if (botaoPressionado) {
botaoPressionado = false;
u8g2.clearBuffer();
u8g2.setCursor(ALIGN_CENTER("MENU"), 13);
u8g2.print(F("MENU"));
u8g2.drawHLine(0, 15, 128);
for (int i = start; i < start + 5 && i < menuLength; i++) {
int yPos = 15 + (i - start) * 12;
if (i == selected) {
u8g2.setDrawColor(1);
u8g2.drawBox(0, yPos + 6, 128, 10);
u8g2.setDrawColor(0);
} else {
u8g2.setDrawColor(1);
}
u8g2.drawXBMP(0, yPos + 6, 10, 10, bitmaps[i]);
u8g2.setCursor(14, yPos + 15);
u8g2.print(options[i]);
}
u8g2.sendBuffer();
}
}
}
void bateria()
{
// Lê a V. bateria
int x = 44; // V. Ficticia (Controle)
int y = ControleRemoto.rcBattery; // V. Ficticia (Carrinho)
if (x >= 50) {
batLevel = maxbat;
} else if (x > 43) {
batLevel = medbat;
} else {
batLevel = lowbat;
}
if (y >= 50) {
RCbatLevel = maxbat;
} else if (y > 43) {
RCbatLevel = medbat;
} else {
RCbatLevel = lowbat;
}
}
Receiver code:
#include "RF24.h"
#include "nRF24L01.h"
#include "Servo.h"
#include <SPI.h>
struct MeuControleRemoto {
boolean BotEsqCim = 0;
boolean BotEsqEsq = 0;
boolean BotEsqDir = 0;
boolean BotEsqBai = 0;
boolean BotDirCim = 0;
boolean BotDirEsq = 0;
boolean BotDirDir = 0;
boolean BotDirBai = 0;
boolean Mts1 = 0;
boolean Mts2 = 0;
boolean Mts3 = 0;
boolean Mts4 = 0;
byte Pot1 = 0;
byte Pot2 = 0;
byte Pot3 = 0;
byte Pot4 = 0;
byte JoyDireitaX = 127;
byte JoyDireitaY = 127;
byte JoyEsquerdaX = 127;
byte JoyEsquerdaY = 127;
byte rcBattery = 50; // 3.7v -> 5v (dividir por 10)
};
MeuControleRemoto ControleRemoto;
Servo servo;
RF24 radio(7, 8);
const byte PonteH1 = 5;
const byte PonteH2 = 6;
uint8_t midpointAX;
uint8_t midpointAY;
uint8_t midpointBX;
uint8_t midpointBY;
uint8_t LastServoPos = 90;
void setup() {
// Serial.begin(9600);
servo.attach(9);
pinMode(PonteH1, OUTPUT);
pinMode(PonteH2, OUTPUT);
radio.begin(); //Inicialisando o MÓDULO RF24l01 para comunicação.
radio.setAutoAck(false); // Desativando pacotes ACK (Pacote de Confirmação de Recebimento de Mensagem)
radio.setChannel(100); // Configurando canal de transmissão do rádio (você pode escolher um canal de 0 a 127)
radio.setDataRate(RF24_250KBPS); // Configurando Módulo para operar em uma taxa de 250kbps (Menor taxa de dados possível para um maior alcance do rádio)
radio.setPALevel(RF24_PA_HIGH); // Configurando Módulo para transmitir em potência máxima (a título de conhecimento, pois aqui o rádio está configurado apenas como receptor)
radio.powerUp(); // Ativando Módulo, caso entre em estado de baixo consumo.
radio.startListening(); //Inicializando o MÓDULO para 'escutar' as requisições enviadas pelo transmissor.
radio.openReadingPipe(1, 0xE8E8F0F0E1LL); // Abrindo o meio de comunicação entre transmissor e receptor, também configurando endereço de comunicação(0xE8E8F0F0E1LL)
midpointAY = 114;
// if (radio.isChipConnected())
// Serial.println("\n\nTransmitter NF24 connected to SPI");
// else Serial.println("\n\nNF24 is NOT connected to SPI");
}
int x = 0;
int y = 0;
void loop() {
/*
Serial.print("1a: ");
Serial.print(ControleRemoto.JoyEsquerdaY);
Serial.print(" | ");
Serial.print("2a: ");
Serial.println(map(ControleRemoto.JoyEsquerdaY, midpointAY,0, 0, 255));
*/
if (radio.available()) { //verificando se há dados enviados pelo tranmissor.
radio.read(&ControleRemoto, sizeof(ControleRemoto)); // Recebendo dados enviado pelo transmissor
if (ControleRemoto.JoyDireitaX != LastServoPos) {
//Serial.println(ControleRemoto.JoyDireitaX);
servo.write(map(ControleRemoto.JoyDireitaX, 0,255, 60,120));
LastServoPos = map(ControleRemoto.JoyDireitaX, 0,255, 60,120);
}
if (ControleRemoto.JoyEsquerdaY > midpointAY + 2) {
analogWrite(PonteH1, map(ControleRemoto.JoyEsquerdaY, midpointAY + 2,255, 20, 255));
digitalWrite(PonteH2, LOW);
}
if (ControleRemoto.JoyEsquerdaY < midpointAY - 2) {
analogWrite(PonteH2, map(ControleRemoto.JoyEsquerdaY, midpointAY - 2,0, 20, 255));
digitalWrite(PonteH1, LOW);
}
Serial.println(map(ControleRemoto.JoyEsquerdaY, midpointAY + 2,255, 20, 255));
Serial.println(map(ControleRemoto.JoyEsquerdaY, midpointAY - 2,0, 20, 255));
}
}