-
Notifications
You must be signed in to change notification settings - Fork 1
/
Transmitter.ino
224 lines (207 loc) · 8.77 KB
/
Transmitter.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/* by HYPERARX (>‿◠)✌
* Arduino Nano Transmitter for RC car
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Wire.h>
// Define the digital inputs
#define b3 2 // button 3
#define b4 3 // button 4
#define t1 4 // Toggle switch 1
#define t2 8 // Toggle switch 2
#define t3 5 // Toggle swich 3
#define t4 6 // Toggle switch 4
#define b1 1 // Button 1
#define b2 7 // Button 2
#define m 0 // button 5 (to initialize mpu6050)
const int MPU = 0x68; // MPU6050 I2C address
float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;
float accAngleX, accAngleY, gyroAngleX, gyroAngleY;
float angleX, angleY;
float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY;
float elapsedTime, currentTime, previousTime;
int c = 0;
RF24 radio(9, 10); // nRF24L01 (CE, CSN)
const uint64_t pipeOut = 0xE9E8F0F0E1LL; //IMPORTANT: The same as in the receiver 0xE9E8F0F0E1LL s
// Max size of this struct is 32 bytes - NRF24L01 buffer limit
struct Data_Package {
byte j1PotX;
byte j1PotY;
byte button3;
byte j2PotX;
byte j2PotY;
byte button4;
byte pot1;
byte pot2;
byte tSwitch1;
byte tSwitch2;
byte tSwitch3;
byte tSwitch4;
byte button1;
byte button2;
};
Data_Package data; //Create a variable with the above structure
void setup() {
Serial.begin(9600);
// Initialize interface to the MPU6050
initialize_MPU6050();
// Call this function if you need to get the IMU error values for your module
//calculate_IMU_error();
// Define the radio communication
radio.begin();
radio.openWritingPipe(pipeOut);
radio.setAutoAck(false);
radio.setDataRate(RF24_250KBPS);
radio.setPALevel(RF24_PA_LOW);
// Activate the Arduino internal pull-up resistors
pinMode(b3, INPUT_PULLUP);
pinMode(b4, INPUT_PULLUP);
pinMode(t1, INPUT_PULLUP);
pinMode(t2, INPUT_PULLUP);
pinMode(t3, INPUT_PULLUP);
pinMode(t4, INPUT_PULLUP);
pinMode(b1, INPUT_PULLUP);
pinMode(b2, INPUT_PULLUP);
pinMode(m, INPUT_PULLUP);
// Set initial default values
data.j1PotX = 127.5; // Values from 0 to 255. When Joystick is in resting position, the value is in the middle, or 127. We actually map the pot value from 0 to 1023 to 0 to 255 because that's one BYTE value
data.j1PotY = 127.5;
data.j2PotX = 127.5;
data.j2PotY = 127.5;
data.button3 = 1;
data.button4 = 1;
data.pot1 = 1;
data.pot2 = 1;
data.tSwitch1 = 1;
data.tSwitch2 = 1;
data.tSwitch3 = 1;
data.tSwitch4 = 1;
data.button1 = 1;
data.button2 = 1;
}
void loop() {
// Read all analog inputs and map them to one Byte value joysticks and rotary potentiometer
data.j1PotX = map(analogRead(A1), 0, 1023, 0, 255); // Convert the analog read value from 0 to 1023 into a BYTE value from 0 to 255
data.j1PotY = map(analogRead(A0), 0, 1023, 0, 255);
data.j2PotX = map(analogRead(A2), 0, 1023, 0, 255);
data.j2PotY = map(analogRead(A3), 0, 1023, 0, 255);
data.pot1 = map(analogRead(A7), 0, 1023, 0, 255);
data.pot2 = map(analogRead(A6), 0, 1023, 0, 255);
// Read all digital inputs tactile & toggle switch
data.button3 = digitalRead(b3);
data.button4 = digitalRead(b4);
data.tSwitch2 = digitalRead(t2);
data.tSwitch1 = digitalRead(t1);
data.tSwitch3 = digitalRead(t3);
data.tSwitch4 = digitalRead(t4);
data.button1 = digitalRead(b1);
data.button2 = digitalRead(b2);
// If toggle switch m is switched on
if (digitalRead(m) == 0) {
read_IMU(); // Use MPU6050 instead of Joystick 1 for controling left, right, forward and backward movements
}
// Send the whole data from the structure to the receiver
radio.write(&data, sizeof(Data_Package));
}
void initialize_MPU6050() {
Wire.begin(); // Initialize comunication
Wire.beginTransmission(MPU); // Start communication with MPU6050 // MPU=0x68
Wire.write(0x6B); // Talk to the register 6B
Wire.write(0x00); // Make reset - place a 0 into the 6B register
Wire.endTransmission(true); //end the transmission
// Configure Accelerometer
Wire.beginTransmission(MPU);
Wire.write(0x1C); //Talk to the ACCEL_CONFIG register
Wire.write(0x10); //Set the register bits as 00010000 (+/- 8g full scale range)
Wire.endTransmission(true);
// Configure Gyro
Wire.beginTransmission(MPU);
Wire.write(0x1B); // Talk to the GYRO_CONFIG register (1B hex)
Wire.write(0x10); // Set the register bits as 00010000 (1000dps full scale)
Wire.endTransmission(true);
}
void calculate_IMU_error() {
// We can call this funtion in the setup section to calculate the accelerometer and gury data error. From here we will get the error values used in the above equations printed on the Serial Monitor.
// Note that we should place the IMU flat in order to get the proper values, so that we then can the correct values
// Read accelerometer values 200 times
while (c < 200) {
Wire.beginTransmission(MPU);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
AccX = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
AccY = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
AccZ = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
// Sum all readings
AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));
AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180 / PI));
c++;
}
//Divide the sum by 200 to get the error value
AccErrorX = AccErrorX / 200;
AccErrorY = AccErrorY / 200;
c = 0;
// Read gyro values 200 times
while (c < 200) {
Wire.beginTransmission(MPU);
Wire.write(0x43);
Wire.endTransmission(false);
Wire.requestFrom(MPU, 4, true);
GyroX = Wire.read() << 8 | Wire.read();
GyroY = Wire.read() << 8 | Wire.read();
// Sum all readings
GyroErrorX = GyroErrorX + (GyroX / 32.8);
GyroErrorY = GyroErrorY + (GyroY / 32.8);
c++;
}
//Divide the sum by 200 to get the error value
GyroErrorX = GyroErrorX / 200;
GyroErrorY = GyroErrorY / 200;
// Print the error values on the Serial Monitor
Serial.print("AccErrorX: ");
Serial.println(AccErrorX);
Serial.print("AccErrorY: ");
Serial.println(AccErrorY);
Serial.print("GyroErrorX: ");
Serial.println(GyroErrorX);
Serial.print("GyroErrorY: ");
Serial.println(GyroErrorY);
}
void read_IMU() {
// === Read acceleromter data === //
Wire.beginTransmission(MPU);
Wire.write(0x3B); // Start with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
//For a range of +-8g, we need to divide the raw values by 4096, according to the datasheet
AccX = (Wire.read() << 8 | Wire.read()) / 4096.0; // X-axis value
AccY = (Wire.read() << 8 | Wire.read()) / 4096.0; // Y-axis value
AccZ = (Wire.read() << 8 | Wire.read()) / 4096.0; // Z-axis value
// Calculating angle values using
accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * 180 / PI) + 1.15; // AccErrorX ~(-1.15) See the calculate_IMU_error()custom function for more details
accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ, 2))) * 180 / PI) - 0.52; // AccErrorX ~(0.5)
// === Read gyro data === //
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
Wire.beginTransmission(MPU);
Wire.write(0x43); // Gyro data first register address 0x43
Wire.endTransmission(false);
Wire.requestFrom(MPU, 4, true); // Read 4 registers total, each axis value is stored in 2 registers
GyroX = (Wire.read() << 8 | Wire.read()) / 32.8; // For a 1000dps range we have to divide first the raw value by 32.8, according to the datasheet
GyroY = (Wire.read() << 8 | Wire.read()) / 32.8;
GyroX = GyroX + 1.85; //// GyroErrorX ~(-1.85)
GyroY = GyroY - 0.15; // GyroErrorY ~(0.15)
// Currently the raw values are in degrees per seconds, deg/s, so we need to multiply by sendonds (s) to get the angle in degrees
gyroAngleX = GyroX * elapsedTime;
gyroAngleY = GyroY * elapsedTime;
// Complementary filter - combine acceleromter and gyro angle values
angleX = 0.98 * (angleX + gyroAngleX) + 0.02 * accAngleX;
angleY = 0.98 * (angleY + gyroAngleY) + 0.02 * accAngleY;
// Map the angle values from -90deg to +90 deg into values from 0 to 255, like the values we are getting from the Joystick
data.j1PotX = map(angleX, -90, +90, 255, 0);
data.j1PotY = map(angleY, -90, +90, 0, 255);
}