丐版3通道带屏体感遥控器制作
仅供学习使用,请勿用于飞行,否则失控炸机后果自负。。。
https://www.bilibili.com/video/BV1n8411C7We/?vd_source=0f4ae06f18a4b63b55bb17a7500b26e3
老外的开源程序,经过删减和修改,使用JY-61等模块制作。
遥控器可接一个0.91inch OLED屏,显示出3轴欧拉角。
接收机可带3个舵机。
遥控器:
#include <SPI.h> // 加载SPI库
#include <Wire.h>
#include <JY901.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <nRF24L01.h> //使用NRF24L01需要用到的库
#include <RF24.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define CE_pin 7 //NRF24L01 CE引脚连接10号引脚(RFnano旧版)D7(RFnano V3)
#define CSN_pin 8 //NRF24L01 CSN引脚接9号引脚(RFnano旧版)D8(RFnano V3)
/////////////////////
float anglex, angley, anglez;
float accx, accy, accz;
float wx, wy, wz;
float accx0, accy0, accz0;
float anglex0, angley0, anglez0;
float wx0, wy0, wz0;
float accAngleX, accAngleY;
float gyrodriftx,gyrodrifty,gyrodriftz;
float elapsedTime, currentTime, previousTime;
int i=1;
struct Data_Package {
byte j1PotX;//byte
byte j1PotY;
byte j1PotZ;
};
Data_Package data; //Create a variable with the above structure
const byte addresses[][6] = {"00001", "00002"}; //NRF24L01地址
RF24 radio(CE_pin, CSN_pin); //配置NRF24L01 CE和CSN引脚
///////////////////////
void setup()
{
Serial.begin(115200);
radio.begin(); //开启NRF24L01通信
radio.openWritingPipe(addresses[1]); //规定NRF24L01地址,发送端和接收端设置相同的地址,并以此方式启用两个模块之间的通信。
radio.setPALevel(RF24_PA_MAX); //设置功率放大器电平
radio.stopListening(); //停止NRF24L01的接收,作为发送端
delay(10);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
while (Serial.available())
{
JY901.CopeSerialData(Serial.read()); //Call JY901 data cope function
}
delay(2000);
for (i=1;i<=50;i+=1)
{
accx0=(float)JY901.stcAcc.a[0]/32768*16+accx0;
accy0=(float)JY901.stcAcc.a[1]/32768*16+accy0;
accz0=(float)JY901.stcAcc.a[2]/32768*16+accz0;
anglex0=(float)JY901.stcAngle.Angle[0]/32768*180+anglex0;
angley0=(float)JY901.stcAngle.Angle[1]/32768*180+angley0;
anglez0=(float)JY901.stcAngle.Angle[2]/32768*180+anglez0;
wx0=(float)JY901.stcGyro.w[0]/32768*2000;
wy0=(float)JY901.stcGyro.w[1]/32768*2000;
wz0=(float)JY901.stcGyro.w[2]/32768*2000;
delay(50);
}
accx0=accx0/50;
accy0=accy0/50;
accz0=accz0/50;
anglex0=anglex0/50;
angley0=angley0/50;
anglez0=anglez0/50;
display.setTextSize(1.2); // Draw 2X-scale text 2倍字体
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0); //显示的坐标位置
display.print("drift wx:");
display.println(wx0);
display.print("drift wy:");
display.println(wy0);
display.print("drift wz:");
display.println(wz0);
display.display();
delay(3000);
display.clearDisplay();
}
void loop()
{
accx=(float)JY901.stcAcc.a[0]/32768*16-accx0;
accy=(float)JY901.stcAcc.a[1]/32768*16-accy0;
accz=(float)JY901.stcAcc.a[2]/32768*16-accz0;
wx=(float)JY901.stcGyro.w[0]/32768*2000-wx0;
wy=(float)JY901.stcGyro.w[1]/32768*2000-wy0;
wz=(float)JY901.stcGyro.w[2]/32768*2000-wz0;
previousTime = currentTime; // Previous time is stored before the actual time read
currentTime = millis(); // Current time actual time read
elapsedTime = (currentTime - previousTime) / 1000; // Divide by 1000 to get seconds
gyrodriftx = wx0 * elapsedTime;
gyrodrifty = wy0 * elapsedTime;
gyrodriftz = wz0 * elapsedTime;
anglex=(float)JY901.stcAngle.Angle[0]/32768*180-anglex0;
angley=(float)JY901.stcAngle.Angle[1]/32768*180-angley0;
anglez=(float)JY901.stcAngle.Angle[2]/32768*180-anglez0;
if (anglez<=-180)
{anglez=360+anglez;}
accAngleX = (-atan(accy / sqrt(pow(accx, 2) + pow(accz, 2))) * 180 / PI) ;
accAngleY = (atan(accx / sqrt(pow(accy, 2) + pow(accz, 2))) * 180 / PI);
anglex=(anglex + gyrodriftx)*0.98 + 0.02 * accAngleX;
angley=(angley + gyrodrifty)*0.98 + 0.02 * accAngleY;
anglez= anglez + gyrodriftz;
display.clearDisplay();
display.setTextSize(1.2); // Draw 2X-scale text 2倍字体
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0); //显示的坐标位置
display.print("GY:");
display.print(wx);
display.print(",");
display.print(wy);
display.print(",");
display.println(wz);
display.print("AC:");
display.print(accx);
display.print(",");
display.print(accy);
display.print(",");
display.println(accz);
display.print("Angle:");
display.print(anglex);
display.print(",");
display.print(angley);
display.print(",");
display.println(anglez);
display.display();
if (anglex<-90){anglex=-90;
}
if (anglex>90){anglex=90;}
if (anglez<-90){anglez=-90;}
if (anglez>90){anglez=90;}
data.j1PotX = map(anglex, -100, +100, 0, 255);
data.j1PotY = map(angley, -100, +100, 0, 255);
data.j1PotZ = map(anglez, -120, +120, 0, 255);
radio.write(&data, sizeof(Data_Package)); //radio.write()函数,将消息发送给接收器,第一个参数是想要发送的变量,通过在变量名之前使用“&”,设置一个指针用于存储我们想要发送的数据的变量,第二个参数设置了我们想要从该变量中获取的字节数。
//radio.write(&anglez, sizeof(anglez));
}
/*
SerialEvent occurs whenever a new data comes in the
hardware serial RX. This routine is run between each
time loop() runs, so using delay inside loop can delay
response. Multiple bytes of data may be available.
*/
void serialEvent()
{
while (Serial.available())
{
JY901.CopeSerialData(Serial.read()); //Call JY901 data cope function
}
}
接收机:
/*
Arduino RC Airplane
== Receiver Code =
by Dejan, www.HowToMechatronics.com
Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Servo.h>
RF24 radio(7, 8); // nRF24L01 (CE, CSN)
const byte addresses[][6] = {"00001", "00002"};
unsigned long lastReceiveTime = 0;
unsigned long currentTime = 0;
Servo rudderServo;
Servo elevatorServo;
Servo aileron1Servo;
int rudderValue, elevatorValue, aileron1Value;
// Max size of this struct is 32 bytes - NRF24L01 buffer limit
struct Data_Package {
byte j1PotX;
byte j1PotY;
byte j1PotZ;
};
Data_Package data; //Create a variable with the above structure
void setup() {
Serial.begin(9600);
radio.begin();
radio.openReadingPipe(1, addresses[1]);
radio.setPALevel(RF24_PA_MAX);
radio.startListening(); // Set the module as receiver
resetData();
rudderServo.attach(6); // CH1
elevatorServo.attach(5); // CH2
aileron1Servo.attach(4); // CH3
}
void loop() {
// Check whether we keep receving data, or we have a connection between the two modules
currentTime = millis();
if ( currentTime - lastReceiveTime > 1000 ) { // If current time is more then 1 second since we have recived the last data, that means we have lost connection
resetData(); // If connection is lost, reset the data. It prevents unwanted behavior, for example if a drone jas a throttle up, if we lose connection it can keep flying away if we dont reset the function
}
// Check whether there is data to be received
if (radio.available()) {
radio.read(&data, sizeof(Data_Package)); // Read the whole data and store it into the 'data' structure
lastReceiveTime = millis(); // At this moment we have received the data
}
// Elevator control
elevatorValue = map(data.j1PotY, 0, 255, 0, 100);
elevatorServo.write(elevatorValue);
// Ailerons control
aileron1Value = map(data.j1PotX, 0, 255, 0, 100);
aileron1Servo.write(aileron1Value);
// Rudder control
rudderValue = map(data.j1PotZ, 0, 255, 0, 100);
rudderServo.write(rudderValue);
}
void resetData() {
// Reset the values when there is no radio connection - Set initial default values
data.j1PotX = 127;
data.j1PotY = 127;
data.j1PotZ = 127;
}