re-factoring/re-structure of modules
This commit is contained in:
BIN
include/.DS_Store
vendored
Normal file
BIN
include/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -17,6 +17,6 @@ debug_tool = stlink
|
||||
board_build.f_cpu = 72000000L
|
||||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
danya0x07/NRF24L01_SimpleLibrary@^1.0.0
|
||||
pkourany/MPU6050@^1.0.3
|
||||
sstaub/Ticker@^3.2.0
|
||||
# danya0x07/NRF24L01_SimpleLibrary@^1.0.0
|
||||
|
||||
|
||||
|
||||
26
rc-micro-car.code-workspace
Normal file
26
rc-micro-car.code-workspace
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"files.associations": {
|
||||
"__bit_reference": "cpp",
|
||||
"__hash_table": "cpp",
|
||||
"__split_buffer": "cpp",
|
||||
"array": "cpp",
|
||||
"deque": "cpp",
|
||||
"filesystem": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iterator": "cpp",
|
||||
"string": "cpp",
|
||||
"string_view": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"ostream": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"ios": "cpp"
|
||||
}
|
||||
}
|
||||
}
|
||||
146
src/MC34933.cpp
146
src/MC34933.cpp
@@ -1,146 +0,0 @@
|
||||
#include <Arduino.h>
|
||||
#include "MC34933.h"
|
||||
|
||||
void MC34933::begin(void)
|
||||
{
|
||||
pinMode(m_MA, OUTPUT);
|
||||
pinMode(m_MB, OUTPUT);
|
||||
//init pins Hi-Z
|
||||
digitalWrite(m_MA, 1);
|
||||
digitalWrite(m_MB, 1);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void MC34933::setDirection(e_direction dir)
|
||||
{
|
||||
m_prevDirection = m_direction;
|
||||
m_direction = dir;
|
||||
}
|
||||
|
||||
void MC34933::start( void )
|
||||
{
|
||||
m_startCmd = true;
|
||||
}
|
||||
|
||||
bool MC34933::start( int speed )
|
||||
{
|
||||
if(setSpeed(speed))
|
||||
{
|
||||
m_startCmd = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MC34933::start( int speed, int duration_ms)
|
||||
{
|
||||
if(setSpeed(speed))
|
||||
{
|
||||
m_startCmd = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MC34933::setSpeed(int speed)
|
||||
{
|
||||
if(speed <= 255)
|
||||
{
|
||||
m_speed = speed;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MC34933::stop ( void )
|
||||
{
|
||||
m_stopCmd = true;
|
||||
}
|
||||
|
||||
void MC34933::forward(int speed)
|
||||
{
|
||||
setDirection(LEFT);
|
||||
if(setSpeed(speed))
|
||||
{
|
||||
start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MC34933::backward(int speed)
|
||||
{
|
||||
setDirection(RIGHT);
|
||||
if(setSpeed(speed))
|
||||
{
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//motor statemachine
|
||||
void MC34933::run( void )
|
||||
{
|
||||
switch(m_status)
|
||||
{
|
||||
case STARTING:
|
||||
{
|
||||
m_startCmd = false; //clear start flag
|
||||
if(m_direction == LEFT)
|
||||
{
|
||||
analogWrite(m_MA, m_speed);
|
||||
digitalWrite(m_MB, 1);
|
||||
}
|
||||
if(m_direction == RIGHT)
|
||||
{
|
||||
analogWrite(m_MB, m_speed);
|
||||
digitalWrite(m_MA, 1);
|
||||
}
|
||||
m_status = RUN;
|
||||
}
|
||||
break;
|
||||
|
||||
case RUN:
|
||||
{
|
||||
if(m_stopCmd )
|
||||
{
|
||||
m_status = STOPPING;
|
||||
|
||||
}
|
||||
|
||||
if(m_direction != m_prevDirection)
|
||||
{
|
||||
m_status = STOPPING;
|
||||
m_startCmd = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STOPPING:
|
||||
{
|
||||
m_stopCmd = false; //clear stopflag
|
||||
//set both pins to break;
|
||||
analogWrite(m_MA, 0);
|
||||
analogWrite(m_MB, 0);
|
||||
digitalWrite(m_MA, 0);
|
||||
digitalWrite(m_MB, 0);
|
||||
m_status = IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
case IDLE:
|
||||
{
|
||||
if(m_startCmd)
|
||||
{
|
||||
m_status = STARTING;
|
||||
}
|
||||
//write pins Hi-z
|
||||
analogWrite(m_MA, 255);
|
||||
analogWrite(m_MB, 255);
|
||||
digitalWrite(m_MA, 1);
|
||||
digitalWrite(m_MB, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#ifndef MC34933MOTORH
|
||||
#define MC34933MOTORH
|
||||
|
||||
enum e_direction
|
||||
{
|
||||
LEFT,
|
||||
RIGHT
|
||||
};
|
||||
|
||||
enum e_status
|
||||
{
|
||||
IDLE,
|
||||
STARTING,
|
||||
RUN,
|
||||
STOPPING
|
||||
};
|
||||
|
||||
class MC34933
|
||||
{
|
||||
private:
|
||||
const int m_MA;
|
||||
const int m_MB;
|
||||
e_direction m_direction = LEFT;
|
||||
e_direction m_prevDirection;
|
||||
int m_speed;
|
||||
e_status m_status;
|
||||
bool m_startCmd = false;
|
||||
bool m_stopCmd = false;
|
||||
|
||||
|
||||
public:
|
||||
MC34933( int pin_ma, int pin_mb)
|
||||
: m_MA{pin_ma}, m_MB{pin_mb} {}
|
||||
|
||||
void begin( void );
|
||||
void setDirection( e_direction dir);
|
||||
e_direction getDirection( void ) {return m_direction;}
|
||||
bool setSpeed( int speed );
|
||||
int getSpeed( void ) {return m_speed;}
|
||||
void start( void );
|
||||
bool start( int speed );
|
||||
bool start( int speed, int duration_ms);
|
||||
void stop ( void );
|
||||
void forward(int speed);
|
||||
void backward(int speed);
|
||||
e_status getStatus( void ) { return m_status;}
|
||||
|
||||
void run(void);
|
||||
|
||||
};
|
||||
|
||||
#endif //MC34933MOTORH
|
||||
241
src/MPU6050.cpp
Normal file
241
src/MPU6050.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
#include "Arduino.h"
|
||||
#include "MPU6050.h"
|
||||
#include <Wire.h>
|
||||
#include "vehicleConfig.h"
|
||||
//#include "radio.h"
|
||||
|
||||
|
||||
|
||||
/* This code is based on Joop Brokkings excellent work:
|
||||
http://www.brokking.net/imu.html
|
||||
https://www.youtube.com/watch?v=4BoIE8YQwM8
|
||||
https://www.youtube.com/watch?v=j-kE0AMEWy4
|
||||
|
||||
I (willumpie82) forked the code from TheDIYGuy999 and have modified it to fit my needs
|
||||
|
||||
This header file is required for the "balancing" (vehicleType = 4) or "MRSC" (vehicleType = 5) options in the vehicleConfig.h
|
||||
Connect an MPU-6050 sensor to GND, VDD (3.3 and 5V compatible), SCL and SDA.
|
||||
Mount it as close as possible to the pivot point of your vehicle
|
||||
|
||||
-->> Note:
|
||||
- The receiver will not work, if vehicleType is set to 4 or 5 and no MPU-6050 sensor is wired up!!
|
||||
- !! Don't move your vehicle during gyro calibration! (about 6s after powering up) !!
|
||||
- The MPU-6050 requires about 20s to stabilize (finding the exact zero point) after powering on!
|
||||
- The measurements are taken with 125Hz (8ms) refresh rate. Reason: processing all the code requires up to
|
||||
7ms loop time with 8MHz MCU clock. --> You can measure your loop time with loopDuration()
|
||||
*/
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// GLOBAL VARIABLES
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
// 6050 variables
|
||||
int gyro_x, gyro_y, gyro_z;
|
||||
long acc_x_raw, acc_y_raw, acc_z_raw;
|
||||
long acc_x, acc_y, acc_z, acc_total_vector;
|
||||
int temperature;
|
||||
long gyro_x_cal, gyro_y_cal, gyro_z_cal;
|
||||
long loop_timer;
|
||||
float angle_pitch, angle_roll;
|
||||
bool set_gyro_angles;
|
||||
float angle_roll_acc, angle_pitch_acc;
|
||||
float yaw_rate;
|
||||
|
||||
|
||||
// configuration variables (you may have to change them)
|
||||
const int calibrationPasses = 500; // 500 is useful
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// PRINT DEBUG DATA
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void writeDebug() {
|
||||
#ifdef DEBUG
|
||||
static unsigned long lastPrint;
|
||||
if (millis() - lastPrint >= 250) {
|
||||
lastPrint = millis();
|
||||
|
||||
Serial.print("P: ");
|
||||
Serial.print(angle_pitch); //Print pitch
|
||||
Serial.print(" R: ");
|
||||
Serial.print(angle_roll); //Print roll
|
||||
Serial.print(" Motor: ");
|
||||
Serial.println(angleOutput); //Print Motor output
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// PROCESS MPU 6050 DATA SUBFUNCTION
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void processMpu6050Data( void ) {
|
||||
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
|
||||
gyro_x -= gyro_x_cal; // Subtract the offset calibration value from the raw gyro_x value
|
||||
gyro_y -= gyro_y_cal; // Subtract the offset calibration value from the raw gyro_y value
|
||||
gyro_z -= gyro_z_cal; // Subtract the offset calibration value from the raw gyro_z value
|
||||
|
||||
//Gyro angle calculations
|
||||
//0.0000611 * 4 = 1 / (125Hz / 65.5)
|
||||
angle_pitch += gyro_x * 0.0004885; // Calculate the traveled pitch angle and add this to the angle_pitch variable
|
||||
angle_roll += gyro_y * 0.0004885; // Calculate the traveled roll angle and add this to the angle_roll variable
|
||||
yaw_rate = gyro_z * 0.0004885; // Yaw rate in degrees per second
|
||||
|
||||
if (cfg->getvehicleType() == balancingthing) { // Those calculations are only required for the self balancing robot. Otherwise we can save some processing time.
|
||||
//0.000001066 = 0.0000611 * (3.142(PI) / 180degr) The Arduino sin function is in radians (not required in this application)
|
||||
//angle_pitch += angle_roll * sin(gyro_z * 0.000001066); // If the IMU has yawed transfer the roll angle to the pitch angle
|
||||
//angle_roll -= angle_pitch * sin(gyro_z * 0.000001066); // If the IMU has yawed transfer the pitch angle to the roll angle
|
||||
|
||||
//Accelerometer reading averaging to improve vibration resistance (added by TheDIYGuy999)
|
||||
acc_x = (acc_x * 9 + acc_x_raw) / 10;
|
||||
acc_y = (acc_y * 9 + acc_y_raw) / 10;
|
||||
acc_z = (acc_z * 9 + acc_z_raw) / 10;
|
||||
|
||||
//Accelerometer angle calculations
|
||||
acc_total_vector = sqrt((acc_x * acc_x) + (acc_y * acc_y) + (acc_z * acc_z)); // Calculate the total accelerometer vector
|
||||
//57.296 = 1 / (3.142 / 180) The Arduino asin function is in radians
|
||||
if (abs(acc_y) < acc_total_vector) { //Prevent the asin function to produce a NaN
|
||||
angle_pitch_acc = asin((float)acc_y / acc_total_vector) * 57.296; //Calculate the pitch angle.
|
||||
}
|
||||
if (abs(acc_x) < acc_total_vector) { //Prevent the asin function to produce a NaN
|
||||
angle_roll_acc = asin((float)acc_x / acc_total_vector) * -57.296; //Calculate the roll angle.
|
||||
}
|
||||
|
||||
if (set_gyro_angles) { // If the IMU is already started (zero drift protection)
|
||||
angle_pitch = angle_pitch * 0.99 + angle_pitch_acc * 0.01; // Correct the drift of the gyro pitch angle with the accelerometer pitch angle
|
||||
angle_roll = angle_roll * 0.99 + angle_roll_acc * 0.01; // Correct the drift of the gyro roll angle with the accelerometer roll angle
|
||||
}
|
||||
else { // At first start
|
||||
angle_pitch = angle_pitch_acc; // Set the gyro pitch angle equals to the accelerometer pitch angle
|
||||
angle_roll = angle_roll_acc; // Set the gyro roll angle equals to the accelerometer roll angle
|
||||
set_gyro_angles = true; // Set the IMU started flag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// READ MPU 6050 RAW DATA FUNCTION
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
// Sub function allows setup to call it without delay
|
||||
void readMpu6050Raw() {
|
||||
Wire.beginTransmission(0x68); // Start communicating with the MPU-6050
|
||||
Wire.write(0x3B); // Send the requested starting register
|
||||
Wire.endTransmission(); // End the transmission
|
||||
Wire.requestFrom(0x68, 14); // Request 14 bytes from the MPU-6050
|
||||
while (Wire.available() < 14); // Wait until all the bytes are received
|
||||
acc_x_raw = Wire.read() << 8 | Wire.read(); // Add the low and high byte to the acc_x variable
|
||||
acc_y_raw = Wire.read() << 8 | Wire.read(); // Add the low and high byte to the acc_y variable
|
||||
acc_z_raw = Wire.read() << 8 | Wire.read(); // Add the low and high byte to the acc_z variable
|
||||
temperature = Wire.read() << 8 | Wire.read(); // Add the low and high byte to the temperature variable
|
||||
gyro_x = Wire.read() << 8 | Wire.read(); // Add the low and high byte to the gyro_x variable
|
||||
gyro_y = Wire.read() << 8 | Wire.read(); // Add the low and high byte to the gyro_y variable
|
||||
gyro_z = Wire.read() << 8 | Wire.read(); // Add the low and high byte to the gyro_z variable
|
||||
}
|
||||
|
||||
// Main function
|
||||
void readMpu6050Data() {
|
||||
static unsigned long lastReading;
|
||||
if (micros() - lastReading >= 8000) { // Read the data every 8000us (equals 125Hz)
|
||||
lastReading = micros();
|
||||
|
||||
readMpu6050Raw(); // Read RAW data
|
||||
|
||||
processMpu6050Data(); // Process the MPU 6050 data
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// MPU 6050 SETUP
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void setupMpu6050() {
|
||||
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
|
||||
Wire.begin(); // Start I2C as master
|
||||
|
||||
// Is an an MPU-6050 (address 0x68) on the I2C bus present?
|
||||
// Allows to use the receiver without an MPU-6050 plugged in (not in balancing mode, vehicleType 4)
|
||||
Wire.beginTransmission(0x68);
|
||||
byte error = Wire.endTransmission(); // Read the I2C error byte
|
||||
if (cfg->getvehicleType() == carwithMRSC && error > 0) { // If MRSC vehicle (5) is active and there is a bus error
|
||||
cfg->setvehicleType(car); // Fall back to vehicle type 0 (car without MPU-6050)
|
||||
return; // Cancel the MPU-6050 setup
|
||||
}
|
||||
|
||||
// Activate the MPU-6050
|
||||
Wire.beginTransmission(0x68); // Start communicating with the MPU-6050
|
||||
Wire.write(0x6B); // Send the requested starting register
|
||||
Wire.write(0x00); // Set the requested starting register
|
||||
Wire.endTransmission(); // End the transmission
|
||||
// Configure the accelerometer (+/-8g)
|
||||
Wire.beginTransmission(0x68); // Start communicating with the MPU-6050
|
||||
Wire.write(0x1C); // Send the requested starting register
|
||||
Wire.write(0x10); // Set the requested starting register
|
||||
Wire.endTransmission(); // End the transmission
|
||||
// Configure the gyro (2000° per second full scale)
|
||||
Wire.beginTransmission(0x68); // Start communicating with the MPU-6050
|
||||
Wire.write(0x1B); // Send the requested starting register
|
||||
Wire.write(0x18); // Set the requested starting register
|
||||
Wire.endTransmission(); // End the transmission
|
||||
|
||||
// Calibrate the gyro (the vehicle must stay steady during this time!)
|
||||
delay(1500); // Delay 1.5 seconds to display the text
|
||||
#ifdef DEBUG
|
||||
Serial.println("Calibrating gyro"); // Print text to console
|
||||
#endif
|
||||
|
||||
int cal_int = 0;
|
||||
while (cal_int < calibrationPasses) { // Run the calibrating code X times
|
||||
static unsigned long lastGyroCal;
|
||||
if (micros() - lastGyroCal >= 8000) { // Read the data every 8000us (equals 125Hz)
|
||||
#ifdef DEBUG
|
||||
if (cal_int % (calibrationPasses / 32) == 0)Serial.print("."); // Print a dot every X readings
|
||||
#endif
|
||||
readMpu6050Raw(); // Read the raw acc and gyro data from the MPU-6050
|
||||
gyro_x_cal += gyro_x; // Add the gyro x-axis offset to the gyro_x_cal variable
|
||||
gyro_y_cal += gyro_y; // Add the gyro y-axis offset to the gyro_y_cal variable
|
||||
gyro_z_cal += gyro_z; // Add the gyro z-axis offset to the gyro_z_cal variable
|
||||
lastGyroCal = micros();
|
||||
cal_int ++;
|
||||
}
|
||||
}
|
||||
gyro_x_cal /= calibrationPasses; // Divide the gyro_x_cal variable by X to get the avarage offset
|
||||
gyro_y_cal /= calibrationPasses; // Divide the gyro_y_cal variable by X to get the avarage offset
|
||||
gyro_z_cal /= calibrationPasses; // Divide the gyro_z_cal variable by X to get the avarage offset
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
Serial.print("X cal: ");
|
||||
Serial.print(gyro_x_cal);
|
||||
Serial.print(" Y cal: ");
|
||||
Serial.print(gyro_y_cal);
|
||||
Serial.print(" Z cal: ");
|
||||
Serial.println(gyro_z_cal);
|
||||
|
||||
Serial.println("done!");
|
||||
#endif
|
||||
}
|
||||
|
||||
float Mpu6050_getangle_pitch( void )
|
||||
{
|
||||
return angle_pitch;
|
||||
}
|
||||
|
||||
float Mpu6050_getyaw_rate( void )
|
||||
{
|
||||
return yaw_rate;
|
||||
}
|
||||
15
src/MPU6050.h
Normal file
15
src/MPU6050.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef MPU6050H
|
||||
#define MPU6050H
|
||||
|
||||
//#include "servos.h"
|
||||
|
||||
void writeDebug();
|
||||
void readMpu6050Data();
|
||||
void setupMpu6050();
|
||||
|
||||
float Mpu6050_getangle_pitch( void );
|
||||
float Mpu6050_getyaw_rate( void );
|
||||
|
||||
|
||||
|
||||
#endif //MPU6050H
|
||||
173
src/balancing.cpp
Normal file
173
src/balancing.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#include <Arduino.h>
|
||||
#include "balancing.h"
|
||||
|
||||
#include "vehicleConfig.h"
|
||||
#include "MPU6050.h"
|
||||
#include "radio.h"
|
||||
#include <../lib/Arduino-PID-Library/PID_v1.h> // https://github.com/br3ttb/Arduino-PID-Library/
|
||||
#include "motors.h"
|
||||
|
||||
int speedPot;
|
||||
|
||||
int speedAveraged;
|
||||
|
||||
|
||||
//Define PID Variables
|
||||
double speedTarget, speedMeasured, speedOutput;
|
||||
double angleTarget, angleMeasured, angleOutput;
|
||||
|
||||
double speedAverage;
|
||||
|
||||
// PID controllers (you may have to change their parameters in balancing() )
|
||||
//Kp: proportional (instantly), Ki: integral (slow, precise), Kd: deriative (speed of difference)
|
||||
PID speedPid(&speedMeasured, &speedOutput, &speedTarget, 0.0, 0.0, 0.0, DIRECT); // Speed: outer, slow control loop
|
||||
PID anglePid(&angleMeasured, &angleOutput, &angleTarget, 0.0, 0.0, 0.0, DIRECT); // Angle: inner, fast control loop
|
||||
|
||||
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// PID SETUP
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void setupPid() {
|
||||
|
||||
// Speed control loop
|
||||
speedPid.SetSampleTime(8); // calcualte every 4ms = 250Hz
|
||||
speedPid.SetOutputLimits(-33, 33); // output range from -33 to 33 (same as in balancing() )
|
||||
speedPid.SetMode(AUTOMATIC);
|
||||
|
||||
// Angle control loop
|
||||
anglePid.SetSampleTime(8); // calcualte every 4ms = 250Hz
|
||||
anglePid.SetOutputLimits(-43, 43); // output range from -43 to 43 for motor
|
||||
anglePid.SetMode(AUTOMATIC);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// BALANCING CALCULATIONS
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void balancing() {
|
||||
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
RcData* data = getDataptr();
|
||||
// Read sensor data
|
||||
readMpu6050Data();
|
||||
|
||||
angleMeasured = Mpu6050_getangle_pitch() - cfg->gettiltCalibration();
|
||||
|
||||
// Read speed pot with 0.2s fader
|
||||
static unsigned long lastPot;
|
||||
if (millis() - lastPot >= 40) { // 40ms
|
||||
lastPot = millis();
|
||||
speedPot = (speedPot * 4 + data->axis3) / 5; // 1:5
|
||||
}
|
||||
|
||||
// PID Parameters (Test)
|
||||
double speedKp = 0.9, speedKi = 0.03, speedKd = 0.0;
|
||||
double angleKp = data->pot1 / 8.0, angleKi = 25.0, angleKd = 0.12; // /You need to connect a potentiometer to the transmitter analog input A6
|
||||
|
||||
// PID Parameters (Working)
|
||||
//double speedKp = 0.9, speedKi = 0.03, speedKd = 0.0;
|
||||
//double angleKp = data.pot1 / 8.0, angleKi = 25.0, angleKd = 0.12; // You need to connect a potentiometer to the transmitter analog input A6
|
||||
|
||||
// Speed PID controller (important to protect the robot from falling over at full motor rpm!)
|
||||
speedTarget = ((float)speedPot - 50.0) / 1.51; // (100 - 50) / 1.51 = Range of about +/- 33 (same as in setupPid() !)
|
||||
speedMeasured = speedAveraged * 1.3; //angleOutput; // 43 / 33 = 1.3
|
||||
speedPid.SetTunings(speedKp, speedKi, speedKd);
|
||||
speedPid.Compute();
|
||||
|
||||
// Angle PID controller
|
||||
angleTarget = speedOutput / -8.25; // 33.0 (from above) / 8.25 = Range of about +/- 4.0° tilt angle
|
||||
// angleTarget = (speedPot - 50) / -12.5; // 50 / 12.5 = Range of about +/- 4.0° tilt angle
|
||||
anglePid.SetTunings(angleKp, angleKi, angleKd);
|
||||
anglePid.Compute();
|
||||
|
||||
// Send the calculated values to the motors
|
||||
driveMotorsBalancing();
|
||||
|
||||
// Display PID variables on the transmitter OLED (for debugging only, comment out the corresponding variables in checkBattery() in this case)
|
||||
//loopDuration(); // compute the loop time
|
||||
//payload.vcc = loopTime;
|
||||
//payload.vcc = angleMeasured;
|
||||
//payload.vcc = speedTarget;
|
||||
//payload.batteryVoltage = speedOutput;
|
||||
//payload.batteryVoltage = speedMeasured;
|
||||
}
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// MRSC (MICRO RC STABILITY CONTROL) CALCULATIONS
|
||||
// =======================================================================================================
|
||||
// For cars with stability control (steering overlay depending on gyro yaw rate)
|
||||
|
||||
void mrsc() {
|
||||
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
RcData* data = getDataptr();
|
||||
//MC34933* Motor1 = getMotorptr(MOTOR1);
|
||||
MC34933* Motor2 = getMotorptr(MOTOR2);
|
||||
|
||||
// Read sensor data
|
||||
readMpu6050Data();
|
||||
|
||||
// If the MRSC gain is a fixed value, read it!
|
||||
#ifdef MRSC_FIXED
|
||||
data.pot1 = mrscGain;
|
||||
#endif
|
||||
|
||||
// Compute steering compensation overlay
|
||||
int turnRateSetPoint = data->axis1 - 50; // turnRateSetPoint = steering angle (0 to 100) - 50 = -50 to 50
|
||||
int turnRateMeasured = Mpu6050_getyaw_rate() * abs(data->axis3 - 50); // degrees/s * speed
|
||||
int steeringAngle = turnRateSetPoint + (turnRateMeasured * data->pot1 / 100); // Compensation depending on the pot value
|
||||
|
||||
steeringAngle = constrain (steeringAngle, -50, 50); // range = -50 to 50
|
||||
|
||||
// Control steering servo (MRSC mode only)
|
||||
#ifdef CONFIG_HAS_SERVO
|
||||
servo1.write(map(steeringAngle, 50, -50, lim1L, lim1R) ); // 45 - 135°
|
||||
#endif //CONFIG_HAS_SERVO
|
||||
// Control motor 2 (steering, not on "High Power" board type)
|
||||
if (!cfg->getHP()) {
|
||||
Motor2->drive((steeringAngle + 50), 0, cfg->getsteeringTorque(), 0, false); // The steering motor (if the original steering motor is reused instead of a servo)
|
||||
}
|
||||
|
||||
// Control motors
|
||||
driveMotorsCar();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void driveMotorsBalancing()
|
||||
{
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
RcData* data = getDataptr();
|
||||
MC34933* Motor1 = getMotorptr(MOTOR1);
|
||||
MC34933* Motor2 = getMotorptr(MOTOR2);
|
||||
|
||||
// The steering overlay is in degrees per second, controlled by the MPU 6050 yaw rate and yoystick axis 1
|
||||
int steering = ((data->axis1 - 50) / 7) - Mpu6050_getyaw_rate(); // -50 to 50 / 8 = 7°/s - yaw_rate
|
||||
steering = constrain(steering, -7, 7);
|
||||
int speed = angleOutput + 50;
|
||||
|
||||
// Calculate averaged motor power (speed) for speed controller feedback
|
||||
static unsigned long lastSpeed;
|
||||
if (millis() - lastSpeed >= 8) { // 8ms
|
||||
speedAveraged = (speedAveraged * 3.0 + angleOutput) / 4.0; // 1:4 (1:8)
|
||||
}
|
||||
|
||||
speed = constrain(speed, 7, 93); // same range as in setupPID() + 50 offset from above!
|
||||
|
||||
if (angleMeasured > -20.0 && angleMeasured < 20.0) { // Only drive motors, if robot stands upright
|
||||
Motor1->drive(speed - steering, cfg->getminPWM(), cfg->getmaxPWMfull(), 0, false); // left caterpillar, 0ms ramp! 50 = neutral!
|
||||
Motor2->drive(speed + steering, cfg->getminPWM(), cfg->getmaxPWMfull(), 0, false); // right caterpillar
|
||||
}
|
||||
else { // keep motors off
|
||||
Motor1->drive(50, cfg->getminPWM(), cfg->getmaxPWMfull(), 0, false); // left caterpillar, 0ms ramp!
|
||||
Motor2->drive(50, cfg->getminPWM(), cfg->getmaxPWMfull(), 0, false); // right caterpillar
|
||||
}
|
||||
}
|
||||
10
src/balancing.h
Normal file
10
src/balancing.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef BALANCINGH
|
||||
#define BALANCINGH
|
||||
|
||||
void balancing( void );
|
||||
void driveMotorsBalancing( void );
|
||||
void setupPid( void );
|
||||
void mrsc();
|
||||
|
||||
|
||||
#endif //BALANCINGH
|
||||
92
src/battery.cpp
Normal file
92
src/battery.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include <arduino.h>
|
||||
#include "battery.h"
|
||||
#include "vehicleConfig.h"
|
||||
#include "rc_board.h"
|
||||
#include "radio.h"
|
||||
#include "motors.h"
|
||||
#include "lights.h"
|
||||
|
||||
|
||||
bool battSense;
|
||||
|
||||
void checkBattery()
|
||||
{
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
ackPayload* payload = getPayloadptr();
|
||||
|
||||
if (cfg->getboardVersion() < 1.2) battSense = false;
|
||||
else battSense = true;
|
||||
|
||||
// switch between load and no load contition
|
||||
if (millis() - getMillisLightsOff() >= 1000) { // one s after the vehicle did stop
|
||||
setIsDriving(false);// = false; // no load
|
||||
}
|
||||
else {
|
||||
setIsDriving(true);// = true; // under load
|
||||
}
|
||||
|
||||
// Every 1000 ms, take measurements
|
||||
static unsigned long lastTrigger;
|
||||
if (millis() - lastTrigger >= 1000) {
|
||||
lastTrigger = millis();
|
||||
|
||||
// Read both averaged voltages
|
||||
payload->batteryVoltage = batteryAverage();
|
||||
payload->vcc = vccAverage();
|
||||
|
||||
if (battSense) { // Observe battery voltage
|
||||
if (payload->batteryVoltage <= cfg->getcutoffVoltage()) payload->batteryOk = false;
|
||||
}
|
||||
else { // Observe vcc voltage
|
||||
if (payload->vcc <= cfg->getcutoffVoltage()) payload->batteryOk = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Voltage read & averaging subfunctions -----------------------------------------
|
||||
// vcc ----
|
||||
float vccAverage()
|
||||
{
|
||||
static int raw[6];
|
||||
|
||||
if (raw[0] == 0) {
|
||||
for (int i = 0; i <= 5; i++) {
|
||||
raw[i] = readVcc(); // Init array
|
||||
}
|
||||
}
|
||||
|
||||
raw[5] = raw[4];
|
||||
raw[4] = raw[3];
|
||||
raw[3] = raw[2];
|
||||
raw[2] = raw[1];
|
||||
raw[1] = raw[0];
|
||||
raw[0] = readVcc();
|
||||
float average = (raw[0] + raw[1] + raw[2] + raw[3] + raw[4] + raw[5]) / 6000.0;
|
||||
return average;
|
||||
}
|
||||
|
||||
// battery ----
|
||||
float batteryAverage()
|
||||
{
|
||||
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
static int raw[6];
|
||||
|
||||
if (!battSense) return 0;
|
||||
|
||||
if (raw[0] == 0) {
|
||||
for (int i = 0; i <= 5; i++) {
|
||||
raw[i] = analogRead(PIN_ADC_VBAT); // Init array
|
||||
}
|
||||
}
|
||||
|
||||
raw[5] = raw[4];
|
||||
raw[4] = raw[3];
|
||||
raw[3] = raw[2];
|
||||
raw[2] = raw[1];
|
||||
raw[1] = raw[0];
|
||||
if (getIsDriving() && cfg->getHP()) raw[0] = (analogRead(PIN_ADC_VBAT) + 31); // add 0.3V while driving (HP version only): 1023 steps * 0.3V / 9.9V = 31
|
||||
else raw[0] = analogRead(PIN_ADC_VBAT); // else take the real voltage (compensates voltage drop while driving)
|
||||
float average = (raw[0] + raw[1] + raw[2] + raw[3] + raw[4] + raw[5]) / 619.999; // 1023steps / 9.9V * 6 = 619.999
|
||||
return average;
|
||||
}
|
||||
10
src/battery.h
Normal file
10
src/battery.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef BATTERYH
|
||||
#define BATTERYH
|
||||
|
||||
void checkBattery();
|
||||
float vccAverage();
|
||||
float batteryAverage();
|
||||
long readVcc();
|
||||
|
||||
|
||||
#endif //BATTERYH
|
||||
23
src/helper.h
Normal file
23
src/helper.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef helper_h
|
||||
#define helper_h
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// LOOP TIME MEASUREMENT
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
unsigned int loopTime;
|
||||
|
||||
|
||||
|
||||
void loopDuration() {
|
||||
static unsigned long timerOld;
|
||||
unsigned long timer = millis();
|
||||
loopTime = timer - timerOld;
|
||||
timerOld = timer;
|
||||
}
|
||||
|
||||
#endif
|
||||
180
src/lights.cpp
Normal file
180
src/lights.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
#include <Arduino.h>
|
||||
#include "lights.h"
|
||||
#include "radio.h"
|
||||
#include "vehicleConfig.h"
|
||||
#include "motors.h"
|
||||
|
||||
#include <../lib/statusLED/statusLED.h> // https://github.com/TheDIYGuy999/statusLED
|
||||
|
||||
|
||||
// Headlight off delay
|
||||
unsigned long millisLightOff = 0;
|
||||
|
||||
|
||||
// Status LED objects
|
||||
statusLED tailLight(false); // "false" = output not inverted
|
||||
statusLED headLight(false);
|
||||
statusLED indicatorL(false);
|
||||
statusLED indicatorR(false);
|
||||
statusLED beaconLights(false);
|
||||
|
||||
// Indicators
|
||||
bool left;
|
||||
bool right;
|
||||
bool hazard;
|
||||
|
||||
bool getLeft( void )
|
||||
{
|
||||
return left;
|
||||
}
|
||||
|
||||
bool getRight( void )
|
||||
{
|
||||
return right;
|
||||
}
|
||||
|
||||
bool getHazard( void )
|
||||
{
|
||||
return hazard;
|
||||
}
|
||||
|
||||
void SetmillisLightOff( unsigned long millis)
|
||||
{
|
||||
millisLightOff = millis;
|
||||
}
|
||||
|
||||
void UpdatemillisLightOff( void )
|
||||
{
|
||||
millisLightOff = millis();
|
||||
}
|
||||
|
||||
|
||||
unsigned long getMillisLightsOff( void )
|
||||
{
|
||||
return millisLightOff;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// LED
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
// Brake light subfunction for ESC vehicles
|
||||
bool escBrakeActive() {
|
||||
RcData* data = getDataptr();
|
||||
static byte driveState;
|
||||
bool brake= false;
|
||||
|
||||
switch (driveState) { // 0 = neutral, 1 = forward, 2 = reverse, 3 = brake
|
||||
|
||||
case 0: // neutral
|
||||
if (data->axis3 > 55) driveState = 1; // forward
|
||||
if (data->axis3 < 45) driveState = 2; // reverse
|
||||
brake = false;
|
||||
break;
|
||||
|
||||
case 1: // forward
|
||||
if (data->axis3 < 45) driveState = 3; // brake
|
||||
brake = false;
|
||||
break;
|
||||
|
||||
case 2: // reverse
|
||||
if (data->axis3 > 55) driveState = 1; // forward
|
||||
brake = false;
|
||||
break;
|
||||
|
||||
case 3: // brake
|
||||
if (data->axis3 > 45) driveState = 2; // go to reverse, if above neutral
|
||||
brake = true;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
brake = false;
|
||||
}
|
||||
|
||||
}
|
||||
return brake;
|
||||
}
|
||||
|
||||
void led()
|
||||
{
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
RcData* data = getDataptr();
|
||||
// Lights are switching off 10s after the vehicle did stop
|
||||
if (millis() - millisLightOff >= 10000) {
|
||||
headLight.off(); // Headlight off
|
||||
tailLight.off(); // Taillight off
|
||||
beaconLights.off(); // Beacons off
|
||||
}
|
||||
else {
|
||||
if (!cfg->getescBrakeLights() && ((!cfg->getHP() && getMotorptr(MOTOR1)->brakeActive()) || (cfg->getHP() && getMotorptr(MOTOR2)->brakeActive()) )) { // if braking detected from TB6612FNG motor driver
|
||||
tailLight.on(); // Brake light (full brightness)
|
||||
}
|
||||
|
||||
else if (cfg->getescBrakeLights() && escBrakeActive() ) { // or braking detected from ESC
|
||||
tailLight.on(); // Brake light (full brightness)
|
||||
}
|
||||
|
||||
else {
|
||||
tailLight.flash(10, 14, 0, 0); // Taillight: 10 on / 14 off = about 40% brightness (soft PWM)
|
||||
}
|
||||
beaconLights.flash(50, 650, 0, 0); // Simulate rotating beacon lights with short flashes
|
||||
headLight.on(); // Headlight on
|
||||
}
|
||||
|
||||
// Indicator lights ----
|
||||
if (cfg->getindicators()) {
|
||||
// Set and reset by lever
|
||||
if (data->axis4 < 5) left = true;
|
||||
if (data->axis4 > 55) left = false;
|
||||
|
||||
if (data->axis4 > 95) right = true;
|
||||
if (data->axis4 < 45) right = false;
|
||||
|
||||
// Reset by steering
|
||||
static int steeringOld;
|
||||
|
||||
if (data->axis1 > steeringOld + 10) {
|
||||
left = false;
|
||||
steeringOld = data->axis1;
|
||||
}
|
||||
|
||||
if (data->axis1 < steeringOld - 10) {
|
||||
right = false;
|
||||
steeringOld = data->axis1;
|
||||
}
|
||||
|
||||
// Lights
|
||||
if (left) { // Left indicator
|
||||
right = false;
|
||||
indicatorL.flash(375, 375, 0, 0);
|
||||
indicatorR.off();
|
||||
}
|
||||
|
||||
if (right) { // Right indicator
|
||||
left = false;
|
||||
indicatorR.flash(375, 375, 0, 0);
|
||||
indicatorL.off();
|
||||
}
|
||||
|
||||
if (hazard) { // Hazard lights
|
||||
if (left) {
|
||||
left = false;
|
||||
indicatorL.off();
|
||||
}
|
||||
if (right) {
|
||||
right = false;
|
||||
indicatorR.off();
|
||||
}
|
||||
indicatorL.flash(375, 375, 0, 0);
|
||||
indicatorR.flash(375, 375, 0, 0);
|
||||
}
|
||||
|
||||
if (!hazard && !left && !right) {
|
||||
indicatorL.off();
|
||||
indicatorR.off();
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/lights.h
Normal file
14
src/lights.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef LIGHTSH
|
||||
#define LIGHTSH
|
||||
|
||||
|
||||
void SetmillisLightOff( unsigned long millis );
|
||||
void UpdatemillisLightOff( void );
|
||||
unsigned long getMillisLightsOff( void );
|
||||
void led();
|
||||
|
||||
bool getLeft( void );
|
||||
bool getRight( void );
|
||||
bool getHazard( void );
|
||||
|
||||
#endif //LIGHTSH
|
||||
362
src/main.cpp
362
src/main.cpp
@@ -1,177 +1,225 @@
|
||||
/*
|
||||
* Blink
|
||||
* Turns on an LED on for one second,
|
||||
* then off for one second, repeatedly.
|
||||
*/
|
||||
// 4 Channel "Micro RC" Receiver with 4 standard RC Servo Outputs
|
||||
// ATMEL Mega 328P TQFP 32 soldered directly to the board, 8MHz external resonator,
|
||||
// 2.4GHz NRF24L01 SMD radio module, TB6612FNG dual dc motor driver
|
||||
// An MPU-6050 gyro / accelerometer can be used for MRSC stability control or self balancing robots
|
||||
// SBUS output on pin TXO
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ticker.h>
|
||||
// See: https://www.youtube.com/playlist?list=PLGO5EJJClJBCjIvu8frS7LrEU3H2Yz_so
|
||||
|
||||
// * * * * N O T E ! The vehicle specific configurations are stored in "vehicleConfig.h" * * * *
|
||||
|
||||
const float codeVersion = 3.4; // Software revision (see https://github.com/TheDIYGuy999/Micro_RC_Receiver/blob/master/README.md)
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// BUILD OPTIONS (comment out unneeded options)
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
//#define DEBUG // if not commented out, Serial.print() is active! For debugging only!!
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// INCLUDE LIRBARIES
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
// Libraries
|
||||
#include <Wire.h> // I2C library (for the MPU-6050 gyro /accelerometer)
|
||||
//#include <RF24.h> // Installed via Tools > Board > Boards Manager > Type RF24
|
||||
#include <Servo.h>
|
||||
#include "vehicleConfig.h"
|
||||
// Tabs (header files in sketch directory)
|
||||
#include "readVCC.h"
|
||||
#include "steeringCurves.h"
|
||||
#include "tone.h"
|
||||
#include "MPU6050.h"
|
||||
#include "helper.h"
|
||||
#include "radio.h"
|
||||
#include "motors.h"
|
||||
#include "battery.h"
|
||||
#include "hall.h"
|
||||
#include "rc_board.h"
|
||||
#include <math.h>
|
||||
#include <I2Cdev.h>
|
||||
#include <MPU6050_6Axis_MotionApps20.h>
|
||||
//#include "Wire.h"
|
||||
#include "MC34933.h"
|
||||
#include "servos.h"
|
||||
#include "balancing.h"
|
||||
#include "lights.h"
|
||||
|
||||
|
||||
bool ms_task_flag = false;
|
||||
bool decims_task_flag = false;
|
||||
bool second_task_flag = false;
|
||||
|
||||
//function prototypes
|
||||
void Tick_handle_msTask (void)
|
||||
|
||||
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// MAIN ARDUINO SETUP (1x during startup)
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void setup()
|
||||
{
|
||||
ms_task_flag = true;
|
||||
}
|
||||
void Tick_handle_10msTask( void )
|
||||
{
|
||||
decims_task_flag = true;
|
||||
}
|
||||
void Tick_handle_1sTask( void )
|
||||
{
|
||||
second_task_flag = true;
|
||||
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
cfg->begin(COKECANCAR);
|
||||
|
||||
RcData* data = getDataptr();
|
||||
|
||||
#ifdef DEBUG
|
||||
Serial.begin(115200);
|
||||
printf_begin();
|
||||
serialCommands = false;
|
||||
delay(3000);
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG
|
||||
// If TXO pin or RXI pin is used for other things, disable Serial
|
||||
if (cfg->getTXO_momentary1() || cfg->getTXO_toggle1() || cfg->getheadLights())
|
||||
{
|
||||
Serial.end(); // make sure, serial is off!
|
||||
#ifndef STM32F1xx
|
||||
UCSR0B = 0b00000000;
|
||||
#endif
|
||||
cfg->serialCommands = false;
|
||||
}
|
||||
else
|
||||
{ // Otherwise use it for serial commands to the light and sound controller
|
||||
|
||||
#ifdef CONFIG_HAS_SBUS
|
||||
SBUS* x8r = getSBUSptr();
|
||||
x8r->begin(); // begin the SBUS communication
|
||||
#else
|
||||
Serial.begin(115200); // begin the standart serial communication
|
||||
#endif
|
||||
cfg->serialCommands = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(cfg->gettoneOut())
|
||||
{
|
||||
R2D2_tell();
|
||||
}
|
||||
|
||||
|
||||
// LED setup
|
||||
#ifdef CONFIG_HAS_LIGHTS
|
||||
if (vehicleType == 4 || vehicleType == 5 ) indicators = false; // Indicators use the same pins as the MPU-6050, so they can't be used in vehicleType 4 or 5!
|
||||
|
||||
if (tailLights) tailLight.begin(A1); // A1 = Servo 2 Pin
|
||||
if (headLights) headLight.begin(0); // 0 = RXI Pin
|
||||
if (indicators) {
|
||||
indicatorL.begin(A4); // A4 = SDA Pin
|
||||
indicatorR.begin(A5); // A5 = SCL Pin
|
||||
}
|
||||
if (beacons) beaconLights.begin(A3); // A3 = Servo 4 Pin
|
||||
#endif //CONFIG_HAS_LIGHTS
|
||||
|
||||
// Radio setup
|
||||
#ifdef CONFIG_HAS_NRF24
|
||||
setupRadio();
|
||||
#endif //CONFIG_HAS_NRF24
|
||||
|
||||
// Servo pins
|
||||
#ifdef CONFIG_HAS_SERVO
|
||||
servo1.attach(A0);
|
||||
if (!tailLights) servo2.attach(A1);
|
||||
if (!engineSound && !toneOut) servo3.attach(A2);
|
||||
if (!beacons) servo4.attach(A3);
|
||||
#endif //CONFIG_HAS_SERVO
|
||||
|
||||
// All axes to neutral position
|
||||
data->axis1 = 50;
|
||||
data->axis2 = 50;
|
||||
data->axis3 = 50;
|
||||
data->axis4 = 50;
|
||||
data->pot1 = 50; // Added in v3.32
|
||||
|
||||
|
||||
|
||||
// Special functions
|
||||
if (cfg->getTXO_momentary1() || cfg->getTXO_toggle1()) pinMode(PIN_SPARE1, OUTPUT);
|
||||
|
||||
// Motor driver setup
|
||||
setupMotors();
|
||||
|
||||
if (cfg->getvehicleType() == balancingthing || cfg->getvehicleType() == carwithMRSC) { // Only for self balancing vehicles and cars with MRSC
|
||||
// MPU 6050 accelerometer / gyro setup
|
||||
setupMpu6050();
|
||||
|
||||
// PID controller setup
|
||||
setupPid();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ticker ms_ticker(Tick_handle_msTask, 1, 0, MILLIS);
|
||||
Ticker decims_ticker(Tick_handle_10msTask, 10, 0, MILLIS);
|
||||
Ticker second_ticker(Tick_handle_1sTask, 1000,0, MILLIS);
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// WRITE DIGITAL OUTPUTS (SPECIAL FUNCTIONS)
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void digitalOutputs() {
|
||||
|
||||
MPU6050 mpu;
|
||||
MC34933 motor1(PIN_M1A,PIN_M1B);
|
||||
MC34933 steering(PIN_M2A, PIN_M2B);
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
RcData* data = getDataptr();
|
||||
static bool wasPressed;
|
||||
|
||||
bool blinkState = false;
|
||||
// Variables will change:
|
||||
int ledState = LOW; // ledState used to set the LED
|
||||
unsigned long previousMillis = 0; // will store last time LED was updated
|
||||
const long interval = 1000; // interval at which to blink (milliseconds)
|
||||
const long main_loop_interval = 10;
|
||||
const long second_interval = 100; //number of decims-ticks
|
||||
unsigned long decims_counter = 0;
|
||||
|
||||
#define MOTORSPEED 1
|
||||
|
||||
|
||||
// ================================================================
|
||||
// === INITIAL SETUP ===
|
||||
// ================================================================
|
||||
|
||||
void handle_msTask (void)
|
||||
{
|
||||
ms_task_flag = true;
|
||||
}
|
||||
|
||||
void handle_10msTask( void )
|
||||
{
|
||||
motor1.run();
|
||||
}
|
||||
|
||||
void handle_1sTask (void )
|
||||
{
|
||||
if (ledState == LOW)
|
||||
{
|
||||
ledState = HIGH;
|
||||
motor1.forward(MOTORSPEED);
|
||||
}
|
||||
else
|
||||
{
|
||||
ledState = LOW;
|
||||
motor1.backward(MOTORSPEED);
|
||||
if (cfg->getTXO_momentary1()) { // only, if momentary function is enabled in vehicle configuration
|
||||
if (data->momentary1) {
|
||||
digitalWrite(PIN_SPARE1, HIGH);
|
||||
#ifdef CONFIG_HAS_TONE
|
||||
R2D2_tell();
|
||||
#endif
|
||||
}
|
||||
Serial.print(".");
|
||||
digitalWrite(PIN_SPARE2, ledState);
|
||||
}
|
||||
|
||||
void init_mpu( void )
|
||||
{
|
||||
// //MPU6050 init
|
||||
// Serial.println("Initializing I2C devices...");
|
||||
// mpu.initialize();
|
||||
|
||||
// Serial.println(F("Initializing DMP..."));
|
||||
// devStatus = mpu.dmpInitialize();
|
||||
|
||||
// // supply your own gyro offsets here, scaled for min sensitivity
|
||||
// mpu.setXGyroOffset(220);
|
||||
// mpu.setYGyroOffset(76);
|
||||
// mpu.setZGyroOffset(-85);
|
||||
// mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
|
||||
|
||||
// // make sure it worked (returns 0 if so)
|
||||
// if (devStatus == 0) {
|
||||
// // turn on the DMP, now that it's ready
|
||||
// Serial.println(F("Enabling DMP..."));
|
||||
// mpu.setDMPEnabled(true);
|
||||
|
||||
// // enable Arduino interrupt detection
|
||||
// Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
|
||||
|
||||
// attachInterrupt(0, dmpDataReady, RISING);
|
||||
|
||||
// mpuIntStatus = mpu.getIntStatus();
|
||||
|
||||
// // set our DMP Ready flag so the main loop() function knows it's okay to use it
|
||||
// Serial.println(F("DMP ready! Waiting for first interrupt..."));
|
||||
// dmpReady = true;
|
||||
|
||||
// // get expected DMP packet size for later comparison
|
||||
// packetSize = mpu.dmpGetFIFOPacketSize();
|
||||
// } else {
|
||||
// // ERROR!
|
||||
// // 1 = initial memory load failed
|
||||
// // 2 = DMP configuration updates failed
|
||||
// // (if it's going to break, usually the code will be 1)
|
||||
// Serial.print(F("DMP Initialization failed (code "));
|
||||
// Serial.print(devStatus);
|
||||
// Serial.println(F(")"));
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
hall_init();
|
||||
|
||||
motor1.begin();
|
||||
delay(5000);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ms_ticker.start();
|
||||
decims_ticker.start();
|
||||
second_ticker.start();
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
||||
ms_ticker.update();
|
||||
decims_ticker.update();
|
||||
second_ticker.update();
|
||||
|
||||
if( ms_task_flag)
|
||||
{
|
||||
ms_task_flag = false;
|
||||
handle_msTask();
|
||||
else digitalWrite(PIN_SPARE1, LOW);
|
||||
}
|
||||
|
||||
if( decims_task_flag)
|
||||
{
|
||||
decims_task_flag = false;
|
||||
handle_10msTask();
|
||||
}
|
||||
if (cfg->getTXO_toggle1()) { // only, if toggle function is enabled in vehicle configuration
|
||||
|
||||
if( second_task_flag)
|
||||
{
|
||||
second_task_flag = false;
|
||||
handle_1sTask();
|
||||
if (data->momentary1 && !wasPressed) {
|
||||
digitalWrite(PIN_SPARE1, !digitalRead(PIN_SPARE1));
|
||||
wasPressed = true;
|
||||
}
|
||||
if (!data->momentary1) wasPressed = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// MAIN LOOP
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void loop() {
|
||||
// Read radio data from transmitter
|
||||
readRadio();
|
||||
|
||||
// Write the servo positions
|
||||
writeServos();
|
||||
|
||||
// Drive the motors
|
||||
drive();
|
||||
|
||||
// Battery check
|
||||
checkBattery();
|
||||
|
||||
// Digital Outputs (special functions)
|
||||
digitalOutputs();
|
||||
|
||||
// LED
|
||||
led();
|
||||
|
||||
// Send serial commands
|
||||
#ifdef CONFIG_HAS_SBUS
|
||||
// Serial commands are transmitted in SBUS standard
|
||||
sendSbusCommands();
|
||||
#else
|
||||
// Normal protocol (for ESP32 engine sound controller only)
|
||||
sendSerialCommands();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
326
src/motors.cpp
Normal file
326
src/motors.cpp
Normal file
@@ -0,0 +1,326 @@
|
||||
#include <Arduino.h>
|
||||
#include "motors.h"
|
||||
#include "vehicleConfig.h"
|
||||
#include "rc_board.h"
|
||||
#include "radio.h"
|
||||
#include "lights.h"
|
||||
#include "steeringCurves.h"
|
||||
#include "balancing.h"
|
||||
|
||||
|
||||
#include "../lib/MC34933/MC34933.h" // https://github.com/willumpie82/MC34933
|
||||
#include <../lib/PWMFrequency/PWMFrequency.h> // https://github.com/TheDIYGuy999/PWMFrequency
|
||||
|
||||
bool isDriving; // is the vehicle driving?
|
||||
// Motor objects
|
||||
MC34933 Motor1;
|
||||
MC34933 Motor2;
|
||||
|
||||
|
||||
// This array is intended for the "vhctype_semi caterpillar" mode. The inner wheel can max. slow down to 60% of the
|
||||
// outer wheels RPM
|
||||
float curvevhctype_semi[][2] = { // see excel sheet!
|
||||
{0, 60} // {input value, output value}
|
||||
, {25, 70}
|
||||
, {50, 80}
|
||||
, {75, 90}
|
||||
, {100, 100}
|
||||
};
|
||||
|
||||
// This array is intended for the "Caterpillar" mode. The inner wheel can spin backwars up to 100% of the
|
||||
// outer wheels RPM. That allows for turning the vehicle "on place"
|
||||
float curveFull[][2] = {
|
||||
{0, -100} // {input value, output value}
|
||||
, {7, 0}
|
||||
, {20, 45}
|
||||
, {60, 85}
|
||||
, {100, 100}
|
||||
};
|
||||
|
||||
// This array is intended for the "Differential Thrust" mode. The inner motor can max. slow down to 20% of the
|
||||
// outer motors RPM
|
||||
float curveThrust[][2] = { // see excel sheet!
|
||||
{0, 20} // {input value, output value}
|
||||
, {25, 40}
|
||||
, {50, 60}
|
||||
, {75, 80}
|
||||
, {100, 100}
|
||||
};
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// MOTOR DRIVER SETUP
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void setupMotors()
|
||||
{
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
// MC34933 H-Bridge pins
|
||||
const byte motor1_in1 = PIN_MOTOR_M1A;
|
||||
const byte motor1_in2 = PIN_MOTOR_M1B;
|
||||
|
||||
const byte motor2_in1 = PIN_MOTOR_M2A;
|
||||
const byte motor2_in2 = PIN_MOTOR_M2B;
|
||||
|
||||
|
||||
// SYNTAX: IN1, IN2, PWM, min. input value, max. input value, neutral position width
|
||||
// invert rotation direction true or false
|
||||
Motor1.begin(motor1_in1, motor1_in2, 0, 100, 4, false); // Drive motor
|
||||
Motor2.begin(motor2_in1, motor2_in2, 0, 100, 4, false); // Steering motor (Drive in "HP" version)
|
||||
|
||||
// Motor PWM frequency prescalers (Requires the PWMFrequency.h library)
|
||||
// Differential steering vehicles: locked to 984Hz, to make sure, that both motors use 984Hz.
|
||||
if (cfg->getvehicleType() == vhctype_semi || cfg->getvehicleType() == vhctype_unknown || cfg->getvehicleType() == simpledualmotorplane)
|
||||
{
|
||||
cfg->setpwmPrescaler2(32);
|
||||
}
|
||||
// ----------- IMPORTANT!! --------------
|
||||
// Motor 1 always runs @ 984Hz PWM frequency and can't be changed, because timers 0 an 1 are in use for other things!
|
||||
// Motor 2 (pin 3) can be changed to the following PWM frequencies: 32 = 984Hz, 8 = 3936Hz, 1 = 31488Hz
|
||||
setPWMPrescaler(3, cfg->getpwmPrescaler2()); // pin 3 is hardcoded, because we can't change all others anyway
|
||||
}
|
||||
|
||||
|
||||
MC34933* getMotorptr(motor_t motor)
|
||||
{
|
||||
if(motor==1)
|
||||
{
|
||||
return &Motor1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return &Motor2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// DRIVE MOTORS CAR (for cars, one motor for driving, one for steering)
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void drive()
|
||||
{
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
|
||||
switch(cfg->getvehicleType())
|
||||
{
|
||||
case car:
|
||||
{
|
||||
driveMotorsCar();
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case carwithMRSC:
|
||||
{
|
||||
mrsc(); // Car with MSRC stabilty control
|
||||
}
|
||||
break;
|
||||
|
||||
case forklift:
|
||||
{
|
||||
driveMotorsForklift(); // Forklift
|
||||
}
|
||||
break;
|
||||
|
||||
case balancingthing:
|
||||
{
|
||||
balancing(); // Self balancing robot
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
driveMotorsSteering(); // Caterpillar and half caterpillar vecicles
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void driveMotorsCar() {
|
||||
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
RcData* data = getDataptr();
|
||||
ackPayload* payload = getPayloadptr();
|
||||
|
||||
int maxPWM;
|
||||
byte maxAcceleration;
|
||||
|
||||
// Speed limitation (max. is 255)
|
||||
if (data->mode1) {
|
||||
maxPWM = cfg->getmaxPWMlimited(); // Limited
|
||||
} else {
|
||||
maxPWM = cfg->getmaxPWMfull(); // Full
|
||||
}
|
||||
|
||||
if (!payload->batteryOk && cfg->getliPo()) data->axis3 = 50; // Stop the vehicle, if the battery is empty!
|
||||
|
||||
// Acceleration & deceleration limitation (ms per 1 step input signal change)
|
||||
if (data->mode2) {
|
||||
maxAcceleration = cfg->getmaxAccelerationLimited(); // Limited
|
||||
} else {
|
||||
maxAcceleration = cfg->getmaxAccelerationFull(); // Full
|
||||
}
|
||||
|
||||
// ***************** Note! The ramptime is intended to protect the gearbox! *******************
|
||||
// SYNTAX: Input value, max PWM, ramptime in ms per 1 PWM increment
|
||||
// false = brake in neutral position inactive
|
||||
|
||||
if (!cfg->getHP()) { // Two channel version: ----
|
||||
if (Motor1.drive(data->axis3, cfg->getminPWM(), maxPWM, maxAcceleration, true) ) { // The drive motor (function returns true, if not in neutral)
|
||||
UpdatemillisLightOff(); // Reset the headlight delay timer, if the vehicle is driving!
|
||||
}
|
||||
if (cfg->getvehicleType() != 5) { // If not car with MSRC stabilty control
|
||||
Motor2.drive(data->axis1, 0, cfg->getsteeringTorque(), 0, false); // The steering motor (if the original steering motor is reused instead of a servo)
|
||||
}
|
||||
}
|
||||
else { // High Power "HP" version. Motor 2 is the driving motor, no motor 1: ----
|
||||
if (Motor2.drive(data->axis3, cfg->getminPWM(), maxPWM, maxAcceleration, true) ) { // The drive motor (function returns true, if not in neutral)
|
||||
UpdatemillisLightOff(); // Reset the headlight delay timer, if the vehicle is driving!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// DRIVE MOTORS FORKLIFT (for forklifts, one motor for driving, one for lifting)
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void driveMotorsForklift() {
|
||||
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
RcData* data = getDataptr();
|
||||
ackPayload* payload = getPayloadptr();
|
||||
|
||||
int maxPWM;
|
||||
byte maxAcceleration;
|
||||
|
||||
// Speed limitation (max. is 255)
|
||||
if (data->mode1) {
|
||||
maxPWM = cfg->getmaxPWMlimited(); // Limited
|
||||
} else {
|
||||
maxPWM = cfg->getmaxPWMfull(); // Full
|
||||
}
|
||||
|
||||
if (!payload->batteryOk && cfg->getliPo()) data->axis3 = 50; // Stop the vehicle, if the battery is empty!
|
||||
|
||||
// Acceleration & deceleration limitation (ms per 1 step input signal change)
|
||||
if (data->mode2) {
|
||||
maxAcceleration = cfg->getmaxAccelerationLimited(); // Limited
|
||||
} else {
|
||||
maxAcceleration = cfg->getmaxAccelerationFull(); // Full
|
||||
}
|
||||
|
||||
// ***************** Note! The ramptime is intended to protect the gearbox! *******************
|
||||
// SYNTAX: Input value, max PWM, ramptime in ms per 1 PWM increment
|
||||
// false = brake in neutral position inactive
|
||||
|
||||
|
||||
if (Motor1.drive(data->axis3, cfg->getminPWM(), maxPWM, maxAcceleration, true) ) { // The drive motor (function returns true, if not in neutral)
|
||||
UpdatemillisLightOff(); // Reset the headlight delay timer, if the vehicle is driving!
|
||||
}
|
||||
Motor2.drive(data->axis2, 0, cfg->getsteeringTorque(), 0, false); // The fork lifting motor (the steering is driven by servo 1)
|
||||
}
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// "STEERING" MOTOR DRIVING FUNCTION (throttle & steering overlay for caterpillar vehicles)
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
bool getIsDriving()
|
||||
{
|
||||
return isDriving;
|
||||
}
|
||||
|
||||
void setIsDriving(bool driveing)
|
||||
{
|
||||
isDriving = driveing;
|
||||
}
|
||||
|
||||
|
||||
void driveMotorsSteering() {
|
||||
|
||||
int pwm[2];
|
||||
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
RcData* data = getDataptr();
|
||||
ackPayload* payload = getPayloadptr();
|
||||
|
||||
// The steering overlay
|
||||
int steeringFactorLeft=0;
|
||||
int steeringFactorRight=0;
|
||||
int steeringFactorLeft2=0;
|
||||
int steeringFactorRight2 =0;
|
||||
|
||||
// The input signal range
|
||||
const int servoMin = 0;
|
||||
const int servoMax = 100;
|
||||
const int servoNeutralMin = 48;
|
||||
const int servoNeutralMax = 52;
|
||||
|
||||
// Compute steering overlay:
|
||||
// The steering signal is channel 1 = data.axis1
|
||||
// 100% = wheel spins with 100% of the requested speed forward
|
||||
// -100% = wheel spins with 100% of the requested speed backward
|
||||
if (data->axis1 <= servoNeutralMin) {
|
||||
steeringFactorLeft = map(data->axis1, servoMin, servoNeutralMin, 0, 100);
|
||||
steeringFactorLeft = constrain(steeringFactorLeft, 0, 100);
|
||||
}
|
||||
else {
|
||||
steeringFactorLeft = 100;
|
||||
}
|
||||
|
||||
if (data->axis1 >= servoNeutralMax) {
|
||||
steeringFactorRight = map(data->axis1, servoMax, servoNeutralMax, 0, 100);
|
||||
steeringFactorRight = constrain(steeringFactorRight, 0, 100);
|
||||
}
|
||||
else {
|
||||
steeringFactorRight = 100;
|
||||
}
|
||||
|
||||
// Nonlinear steering overlay correction
|
||||
if (cfg->getvehicleType() == 6) {
|
||||
steeringFactorLeft2 = reMap(curveThrust, steeringFactorLeft); // Differential thrust mode
|
||||
steeringFactorRight2 = reMap(curveThrust, steeringFactorRight);
|
||||
data->axis3 = constrain(data->axis3, 50, 100); // reverse locked!
|
||||
}
|
||||
if (cfg->getvehicleType() == 2) {
|
||||
steeringFactorLeft2 = reMap(curveFull, steeringFactorLeft); // Caterpillar mode
|
||||
steeringFactorRight2 = reMap(curveFull, steeringFactorRight);
|
||||
}
|
||||
if (cfg->getvehicleType() == 1) {
|
||||
steeringFactorLeft2 = reMap(curvevhctype_semi, steeringFactorLeft); // vhctype_semi caterpillar mode
|
||||
steeringFactorRight2 = reMap(curvevhctype_semi, steeringFactorRight);
|
||||
}
|
||||
|
||||
// Drive caterpillar motors
|
||||
// The throttle signal (for both caterpillars) is channel 3 = data.axis3
|
||||
// -100 to 100%
|
||||
pwm[0] = map(data->axis3, servoMin, servoMax, 100, -100) * steeringFactorRight2 / 100;
|
||||
pwm[1] = map(data->axis3, servoMin, servoMax, 100, -100) * steeringFactorLeft2 / 100;
|
||||
|
||||
pwm[0] = map(pwm[0], 100, -100, 100, 0); // convert -100 to 100% to 0-100 for motor control
|
||||
pwm[1] = map(pwm[1], 100, -100, 100, 0);
|
||||
|
||||
if (!payload->batteryOk && cfg->getliPo()) { // Both motors off, if LiPo battery is empty!
|
||||
pwm[0] = 0;
|
||||
pwm[1] = 0;
|
||||
}
|
||||
|
||||
Motor1.drive(pwm[0], 0, 255, 0, false); // left caterpillar, 0ms ramp!
|
||||
Motor2.drive(pwm[1], 0, 255, 0, false); // right caterpillar
|
||||
|
||||
if (pwm[0] < 40 || pwm[0] > 60 || pwm[1] < 40 || pwm[1] > 60) {
|
||||
UpdatemillisLightOff(); // Reset the headlight delay timer, if the vehicle is driving!
|
||||
}
|
||||
}
|
||||
25
src/motors.h
Normal file
25
src/motors.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef MOTORSH
|
||||
#define MOTORSH
|
||||
|
||||
#include "../lib/MC34933/MC34933.h" // https://github.com/willumpie82/MC34933
|
||||
|
||||
typedef enum motor_e
|
||||
{
|
||||
MOTOR1,
|
||||
MOTOR2
|
||||
}motor_t;
|
||||
|
||||
|
||||
// Motors
|
||||
bool getIsDriving();
|
||||
void setIsDriving(bool driveing);
|
||||
|
||||
void setupMotors();
|
||||
void driveMotorsCar();
|
||||
void driveMotorsForklift();
|
||||
void driveMotorsSteering();
|
||||
void drive();
|
||||
|
||||
MC34933* getMotorptr(motor_t motor);
|
||||
|
||||
#endif //MOTORSH
|
||||
217
src/radio.cpp
Normal file
217
src/radio.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
#include <arduino.h>
|
||||
#include "radio.h"
|
||||
#include "lights.h"
|
||||
#include "vehicleConfig.h"
|
||||
#include "rc_board.h"
|
||||
|
||||
#include "../../lib/SBUS/src/SBUS.h"
|
||||
|
||||
// Hardware configuration: Set up nRF24L01 radio on hardware SPI bus & pins 8 (CE) & 7 (CSN)
|
||||
#ifdef CONFIG_HAS_NRF24
|
||||
RF24 radio(8, 7);
|
||||
#endif
|
||||
|
||||
// SBUS object
|
||||
#ifdef CONFIG_HAS_SBUS
|
||||
#ifndef DEBUG
|
||||
SBUS x8r(Serial);
|
||||
|
||||
SBUS* getSBUSptr( void ){return &x8r;}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
RcData data;
|
||||
|
||||
RcData* getDataptr(void)
|
||||
{
|
||||
return &data;
|
||||
}
|
||||
// Radio channels (126 channels are supported)
|
||||
byte chPointer = 0; // Channel 1 (the first entry of the array) is active by default
|
||||
const byte NRFchannel[] {
|
||||
1, 2
|
||||
};
|
||||
|
||||
// the ID number of the used "radio pipe" must match with the selected ID on the transmitter!
|
||||
// 10 ID's are available @ the moment
|
||||
const uint64_t pipeIn[] = {
|
||||
0xE9E8F0F0B1LL, 0xE9E8F0F0B2LL, 0xE9E8F0F0B3LL, 0xE9E8F0F0B4LL, 0xE9E8F0F0B5LL,
|
||||
0xE9E8F0F0B6LL, 0xE9E8F0F0B7LL, 0xE9E8F0F0B8LL, 0xE9E8F0F0B9LL, 0xE9E8F0F0B0LL
|
||||
};
|
||||
const int maxVehicleNumber = (sizeof(pipeIn) / (sizeof(uint64_t)));
|
||||
|
||||
ackPayload payload;
|
||||
|
||||
ackPayload* getPayloadptr( void )
|
||||
{
|
||||
return &payload;
|
||||
}
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// RADIO SETUP
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void setupRadio() {
|
||||
#ifdef CONFIG_HAS_NRF24
|
||||
|
||||
radio.begin();
|
||||
radio.setChannel(NRFchannel[chPointer]);
|
||||
|
||||
// Set Power Amplifier (PA) level to one of four levels: RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX
|
||||
radio.setPALevel(RF24_PA_HIGH); // HIGH
|
||||
|
||||
radio.setDataRate(RF24_250KBPS);
|
||||
radio.setAutoAck(pipeIn[vehicleNumber - 1], true); // Ensure autoACK is enabled
|
||||
radio.enableAckPayload();
|
||||
radio.enableDynamicPayloads();
|
||||
radio.setRetries(5, 5); // 5x250us delay (blocking!!), max. 5 retries
|
||||
//radio.setCRCLength(RF24_CRC_8); // Use 8-bit CRC for performance
|
||||
|
||||
#ifdef DEBUG
|
||||
radio.printDetails();
|
||||
delay(3000);
|
||||
#endif
|
||||
|
||||
radio.openReadingPipe(1, pipeIn[vehicleNumber - 1]);
|
||||
radio.startListening();
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// READ RADIO DATA
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void readRadio() {
|
||||
#ifdef CONFIG_HAS_NRF24
|
||||
|
||||
static unsigned long lastRecvTime = 0;
|
||||
byte pipeNo;
|
||||
|
||||
if (radio.available(&pipeNo)) {
|
||||
radio.writeAckPayload(pipeNo, &payload, sizeof(struct ackPayload) ); // prepare the ACK payload
|
||||
radio.read(&data, sizeof(struct RcData)); // read the radia data and send out the ACK payload
|
||||
hazard = false;
|
||||
lastRecvTime = millis();
|
||||
#ifdef DEBUG
|
||||
Serial.print(data.axis1);
|
||||
Serial.print("\t");
|
||||
Serial.print(data.axis2);
|
||||
Serial.print("\t");
|
||||
Serial.print(data.axis3);
|
||||
Serial.print("\t");
|
||||
Serial.print(data.axis4);
|
||||
Serial.println("\t");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Switch channel
|
||||
if (millis() - lastRecvTime > 500) {
|
||||
chPointer ++;
|
||||
if (chPointer >= sizeof((*NRFchannel) / sizeof(byte))) chPointer = 0;
|
||||
radio.setChannel(NRFchannel[chPointer]);
|
||||
payload.channel = NRFchannel[chPointer];
|
||||
}
|
||||
|
||||
if (millis() - lastRecvTime > 1000) { // set all analog values to their middle position, if no RC signal is received during 1s!
|
||||
data.axis1 = 50; // Aileron (Steering for car)
|
||||
data.axis2 = 50; // Elevator
|
||||
data.axis3 = 50; // Throttle
|
||||
data.axis4 = 50; // Rudder
|
||||
hazard = true; // Enable hazard lights
|
||||
payload.batteryOk = true; // Clear low battery alert (allows to re-enable the vehicle, if you switch off the transmitter)
|
||||
#ifdef DEBUG
|
||||
Serial.println("No Radio Available - Check Transmitter!");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (millis() - lastRecvTime > 2000) {
|
||||
setupRadio(); // re-initialize radio
|
||||
lastRecvTime = millis();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// SBUS COMMANDS TO LIGHT- & SOUND CONTROLLER (if not DEBUG, not TXO_momentary1, not TXO_toggle1, not headLights)
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void sendSbusCommands()
|
||||
{
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
|
||||
static unsigned long lastSbusTime;
|
||||
uint16_t channels[16];
|
||||
|
||||
// See: https://github.com/TheDIYGuy999/Rc_Engine_Sound_ESP32
|
||||
|
||||
if (cfg->serialCommands) { // only, if we are in serial command mode
|
||||
if (millis() - lastSbusTime > 14) { // Send the data every 14ms
|
||||
lastSbusTime = millis();
|
||||
|
||||
// Fill SBUS packet with our channels
|
||||
|
||||
// Proportional channels
|
||||
channels[0] = map(data.axis1, 0, 100, 172, 1811);
|
||||
channels[1] = map(data.axis2, 0, 100, 172, 1811);
|
||||
channels[2] = map(data.axis3, 0, 100, 172, 1811);
|
||||
channels[3] = map(data.axis4, 0, 100, 172, 1811);
|
||||
channels[4] = map(data.pot1, 0, 100, 172, 1811);
|
||||
|
||||
// Switches etc.
|
||||
if (data.mode1) channels[5] = 1811; else channels[5] = 172;
|
||||
if (data.mode2) channels[6] = 1811; else channels[6] = 172;
|
||||
if (data.momentary1) channels[7] = 1811; else channels[7] = 172;
|
||||
if (getHazard()) channels[8] = 1811; else channels[8] = 172;
|
||||
if (getLeft()) channels[9] = 1811; else channels[9] = 172;
|
||||
if (getRight()) channels[10] = 1811; else channels[10] = 172;
|
||||
|
||||
// write the SBUS packet
|
||||
#ifdef CONFIG_HAS_SBUS
|
||||
x8r.write(&channels[0]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// SERIAL COMMANDS TO LIGHT- & SOUND CONTROLLER (if not DEBUG, not TXO_momentary1, not TXO_toggle1, not headLights)
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void sendSerialCommands()
|
||||
{
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
|
||||
static unsigned long lastSerialTime;
|
||||
|
||||
// '\n' is used as delimiter (separator of variables) during parsing on the sound controller
|
||||
// it is generated by the "println" (print line) command!
|
||||
// See: https://github.com/TheDIYGuy999/Rc_Engine_Sound_ESP32
|
||||
|
||||
if (cfg->serialCommands) { // only, if we are in serial command mode
|
||||
if (millis() - lastSerialTime > 20) { // Send the data every 20ms
|
||||
lastSerialTime = millis();
|
||||
Serial.print('<'); // Start marker
|
||||
Serial.println(data.axis1);
|
||||
Serial.println(data.axis2);
|
||||
Serial.println(data.axis3);
|
||||
Serial.println(data.axis4);
|
||||
Serial.println(data.pot1);
|
||||
Serial.println(data.mode1);
|
||||
Serial.println(data.mode2);
|
||||
Serial.println(data.momentary1);
|
||||
Serial.println(getHazard());
|
||||
Serial.println(getLeft());
|
||||
Serial.println(getRight());
|
||||
Serial.print('>'); // End marker
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/radio.h
Normal file
44
src/radio.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef RADIOH
|
||||
#define RADIOH
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "../../lib/SBUS/src/SBUS.h"
|
||||
|
||||
|
||||
|
||||
|
||||
// The size of this struct should not exceed 32 bytes
|
||||
struct RcData {
|
||||
byte axis1; // Aileron (Steering for car)
|
||||
byte axis2; // Elevator
|
||||
byte axis3; // Throttle
|
||||
byte axis4; // Rudder
|
||||
bool mode1 = false; // Mode1 (toggle speed limitation)
|
||||
bool mode2 = false; // Mode2 (toggle acc. / dec. limitation)
|
||||
bool momentary1 = false; // Momentary push button
|
||||
byte pot1; // Potentiometer
|
||||
};
|
||||
|
||||
|
||||
// This struct defines data, which are embedded inside the ACK payload
|
||||
struct ackPayload {
|
||||
float vcc; // vehicle vcc voltage
|
||||
float batteryVoltage; // vehicle battery voltage
|
||||
bool batteryOk = true; // the vehicle battery voltage is OK!
|
||||
byte channel = 1; // the channel number
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Function prototypes
|
||||
ackPayload* getPayloadptr( void );
|
||||
RcData* getDataptr(void);
|
||||
void readRadio( void );
|
||||
void setupRadio( void );
|
||||
|
||||
void sendSerialCommands( void );
|
||||
SBUS* getSBUSptr( void );
|
||||
void sendSbusCommands( void );
|
||||
|
||||
|
||||
#endif // RADIOH
|
||||
@@ -1,6 +1,13 @@
|
||||
#ifndef RCBOARDH
|
||||
#define RCBOARDH
|
||||
|
||||
//#define CONFIG_HAS_TONE
|
||||
//#define CONFIG_HAS_NRF24
|
||||
#define CONFIG_HAS_SBUS
|
||||
//#define CONFIG_HAS_LIGHTS
|
||||
//#define CONFIG_HAS_SERVO
|
||||
|
||||
|
||||
#define PIN_ADC_VBAT PA0
|
||||
//NRF24 pins
|
||||
#define PIN_NRF_CE PA1
|
||||
@@ -16,10 +23,10 @@
|
||||
#define PIN_MPU_SCL PB6
|
||||
|
||||
//MC34933 motordrive pins
|
||||
#define PIN_M2A PA15
|
||||
#define PIN_M2B PA12
|
||||
#define PIN_M1A PA10
|
||||
#define PIN_M1B PA11
|
||||
#define PIN_MOTOR_M2A PA15
|
||||
#define PIN_MOTOR_M2B PA12
|
||||
#define PIN_MOTOR_M1A PA10
|
||||
#define PIN_MOTOR_M1B PA11
|
||||
|
||||
//CPU pins
|
||||
#define PIN_MCU_BOOT1 PB2
|
||||
|
||||
40
src/readVCC.cpp
Normal file
40
src/readVCC.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "Arduino.h"
|
||||
#include "rc_board.h"
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// READ VCC VOLTAGE
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
long readVcc() {
|
||||
// Read 1.1V reference against AVcc
|
||||
// set the reference to Vcc and the measurement to the internal 1.1V reference
|
||||
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
|
||||
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
|
||||
ADMUX = _BV(MUX5) | _BV(MUX0);
|
||||
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
|
||||
ADMUX = _BV(MUX3) | _BV(MUX2);
|
||||
#elif defined (STM32F1xx)
|
||||
//nothing to set
|
||||
#else
|
||||
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
|
||||
#endif
|
||||
|
||||
#if defined (STM32F1xx)
|
||||
long result = analogRead(PIN_ADC_VBAT);
|
||||
#else
|
||||
delayMicroseconds(500); // Wait for Vref to settle (was delay(2) analogRead() will be affected, if < 300micros!!
|
||||
ADCSRA |= _BV(ADSC); // Start conversion
|
||||
while (bit_is_set(ADCSRA, ADSC)); // measuring
|
||||
|
||||
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
|
||||
uint8_t high = ADCH; // unlocks both
|
||||
|
||||
long result = (high << 8) | low;
|
||||
#endif
|
||||
result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
|
||||
return result; // Vcc in millivolts
|
||||
|
||||
}
|
||||
6
src/readVCC.h
Normal file
6
src/readVCC.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef readVCC_h
|
||||
#define readVCC_h
|
||||
|
||||
long readVcc();
|
||||
|
||||
#endif //readVCC_h
|
||||
72
src/servos.cpp
Normal file
72
src/servos.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <Arduino.h>
|
||||
#include "servos.h"
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// WRITE SERVO POSITIONS
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
void writeServos()
|
||||
{
|
||||
#ifdef CONFIG_HAS_SERVO
|
||||
// Aileron or Steering
|
||||
if (vehicleType != 5) { // If not car with MSRC stabilty control
|
||||
servo1.write(map(data.axis1, 100, 0, lim1L, lim1R) ); // 45 - 135°
|
||||
}
|
||||
|
||||
// Elevator or shifting gearbox actuator
|
||||
#ifdef TWO_SPEED_GEARBOX // Shifting gearbox mode, controlled by "Mode 1" button
|
||||
if (!tailLights) {
|
||||
if (data.mode1)servo2.write(lim2L);
|
||||
else servo2.write(lim2R);
|
||||
}
|
||||
|
||||
#else
|
||||
#ifdef THREE_SPEED_GEARBOX // Shifting gearbox mode, controlled by 3 position switch
|
||||
if (!tailLights) {
|
||||
if (data.axis2 < 10)servo2.write(lim2R);
|
||||
else if (data.axis2 > 90)servo2.write(lim2L);
|
||||
else servo2.write(lim2C);
|
||||
}
|
||||
|
||||
#else // Servo controlled by joystick CH2
|
||||
if (!tailLights) servo2.write(map(data.axis2, 100, 0, lim2L, lim2R) ); // 45 - 135°
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Throttle (for ESC control, if you don't use the internal TB6612FNG motor driver)
|
||||
if (data.mode1) { // limited speed!
|
||||
servo3.write(map(data.axis3, 100, 0, lim3Llow, lim3Rlow ) ); // less than +/- 45°
|
||||
}
|
||||
else { // full speed!
|
||||
servo3.write(map(data.axis3, 100, 0, lim3L, lim3R) ); // 45 - 135°
|
||||
}
|
||||
|
||||
// Rudder or trailer unlock actuator
|
||||
#ifdef TRACTOR_TRAILER_UNLOCK // Tractor trailer unlocking, controlled by "Momentary 1" ("Back / Pulse") button
|
||||
if (!beacons && !potentiometer1) {
|
||||
if (data.momentary1)servo4.write(lim4L);
|
||||
else servo4.write(lim4R);
|
||||
}
|
||||
|
||||
#else // Servo controlled by joystick CH4
|
||||
if (!potentiometer1) { // Servo 4 controlled by CH4
|
||||
if (!beacons) servo4.write(map(data.axis4, 100, 0, lim4L, lim4R) ); // 45 - 135°
|
||||
}
|
||||
else { // Servo 4 controlled by transmitter potentiometer knob
|
||||
if (!beacons) servo4.write(map(data.pot1, 0, 100, 45, 135) ); // 45 - 135°
|
||||
}
|
||||
#endif
|
||||
|
||||
// Axis 2 on the joystick switches engine sound on servo channel 3 on and off!
|
||||
if (engineSound) {
|
||||
if (data.axis2 > 80) {
|
||||
servo3.attach(A2); // Enable servo 3 pulse
|
||||
}
|
||||
if (data. axis2 < 20) {
|
||||
servo3.detach(); // Disable servo 3 pulse = engine off signal for "TheDIYGuy999" engine simulator!
|
||||
}
|
||||
}
|
||||
#endif //CONFIG_HAS_SERVO
|
||||
}
|
||||
6
src/servos.h
Normal file
6
src/servos.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef SERVOSH
|
||||
#define SERVOSH
|
||||
|
||||
void writeServos();
|
||||
|
||||
#endif //SERVOSH
|
||||
35
src/steeringCurves.cpp
Normal file
35
src/steeringCurves.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <Arduino.h>
|
||||
#include "steeringCurves.h"
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// NONLINEAR ARRAYS FOR STEERING OVERLAY
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
// In order to optimize the steering behaviour for your vehicle, just change the steering curves in the arrays below
|
||||
|
||||
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// ARRAY INTERPOLATION
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
// Credit: http://interface.khm.de/index.php/lab/interfaces-advanced/nonlinear-mapping/
|
||||
|
||||
int reMap(float pts[][2], int input) {
|
||||
int rr = 0;
|
||||
float mm;
|
||||
|
||||
for (int nn = 0; nn < 5; nn++) {
|
||||
if (input >= pts[nn][0] && input <= pts[nn + 1][0]) {
|
||||
mm = ( pts[nn][1] - pts[nn + 1][1] ) / ( pts[nn][0] - pts[nn + 1][0] );
|
||||
mm = mm * (input - pts[nn][0]);
|
||||
mm = mm + pts[nn][1];
|
||||
rr = mm;
|
||||
}
|
||||
}
|
||||
return (rr);
|
||||
}
|
||||
7
src/steeringCurves.h
Normal file
7
src/steeringCurves.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef steeringCurves_h
|
||||
#define steeringCurves_h
|
||||
|
||||
|
||||
int reMap(float pts[][2], int input);
|
||||
|
||||
#endif //steeringCurves_h
|
||||
32
src/tone.cpp
Normal file
32
src/tone.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <Arduino.h>
|
||||
#include "tone.h"
|
||||
#include "vehicleConfig.h"
|
||||
|
||||
|
||||
//
|
||||
// =======================================================================================================
|
||||
// TONE FUNCTION FOR STAR WARS R2-D2
|
||||
// =======================================================================================================
|
||||
//
|
||||
|
||||
// Engine sound
|
||||
bool engineOn = false;
|
||||
|
||||
int r2d2Tones[] = {
|
||||
3520, 3136, 2637, 2093, 2349, 3951, 2794, 4186
|
||||
};
|
||||
|
||||
void R2D2_tell()
|
||||
{
|
||||
|
||||
vehicleConfig* cfg = getConfigptr();
|
||||
|
||||
if (cfg->gettoneOut()) {
|
||||
for (int notePlay = 0; notePlay < 8; notePlay++) {
|
||||
int noteRandom = random(7);
|
||||
tone(A2, r2d2Tones[noteRandom], 80); // Pin, frequency, duration
|
||||
delay(50);
|
||||
noTone(A2);
|
||||
}
|
||||
}
|
||||
}
|
||||
8
src/tone.h
Normal file
8
src/tone.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef tone_h
|
||||
#define tone_h
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
void R2D2_tell();
|
||||
|
||||
#endif
|
||||
2978
src/vehicleConfig.cpp
Normal file
2978
src/vehicleConfig.cpp
Normal file
File diff suppressed because it is too large
Load Diff
183
src/vehicleConfig.h
Normal file
183
src/vehicleConfig.h
Normal file
@@ -0,0 +1,183 @@
|
||||
|
||||
#ifndef vehicleConfig_h
|
||||
#define vehicleConfig_h
|
||||
|
||||
typedef enum vehicleconfig_e
|
||||
{
|
||||
STANDARDCAR,
|
||||
DRIFTCAR,
|
||||
COKECANCAR
|
||||
}vehicleconfig_t;
|
||||
|
||||
typedef enum vehicletype_e
|
||||
{
|
||||
car = 0,
|
||||
vhctype_semi = 1,
|
||||
vhctype_unknown = 2,
|
||||
forklift = 3,
|
||||
balancingthing = 4,
|
||||
carwithMRSC = 5,
|
||||
simpledualmotorplane = 6
|
||||
}vehicletype_t;
|
||||
|
||||
class vehicleConfig
|
||||
{
|
||||
bool m_liPo;
|
||||
float m_cutoffVoltage; // trigger, as soon as VCC drops! (no battery sensing)
|
||||
|
||||
// Board type
|
||||
float m_boardVersion;
|
||||
bool m_HP;
|
||||
|
||||
// Vehicle address
|
||||
int m_vehicleNumber;
|
||||
|
||||
// Vehicle type
|
||||
vehicletype_t m_vehicleType;
|
||||
|
||||
// Lights
|
||||
bool m_escBrakeLights;
|
||||
bool m_tailLights;
|
||||
bool m_headLights;
|
||||
bool m_indicators;
|
||||
bool m_beacons;
|
||||
|
||||
// Servo limits
|
||||
byte m_lim1L;
|
||||
byte m_lim1R;
|
||||
byte m_lim2L;
|
||||
byte m_lim2R;
|
||||
byte m_lim3L;
|
||||
byte m_lim3R;
|
||||
byte m_lim3Llow;
|
||||
byte m_lim3Rlow;
|
||||
byte m_lim4L;
|
||||
byte m_lim4R;
|
||||
|
||||
// Motor configuration
|
||||
int m_maxPWMfull;
|
||||
int m_maxPWMlimited;
|
||||
int m_minPWM;
|
||||
byte m_maxAccelerationFull;
|
||||
byte m_maxAccelerationLimited;
|
||||
|
||||
// Variables for self balancing (vehicleType = 4) only!
|
||||
float m_tiltCalibration;
|
||||
// Steering configuration
|
||||
byte m_steeringTorque;
|
||||
|
||||
// Motor 2 PWM frequency
|
||||
byte m_pwmPrescaler2;
|
||||
|
||||
// Additional Channels
|
||||
bool m_TXO_momentary1;
|
||||
bool m_TXO_toggle1;
|
||||
bool m_potentiometer1;
|
||||
|
||||
// Engine sound
|
||||
bool m_engineSound;
|
||||
|
||||
// Tone sound
|
||||
bool m_toneOut;
|
||||
|
||||
|
||||
public:
|
||||
vehicleConfig() {serialCommands= false;};
|
||||
|
||||
bool serialCommands;
|
||||
|
||||
|
||||
void begin(vehicleconfig_t vehicle);
|
||||
|
||||
void setBasicParams(
|
||||
bool liPo,
|
||||
float cutoffVoltage,
|
||||
float boardVersion,
|
||||
bool HP,
|
||||
int vehicleNumber,
|
||||
vehicletype_t vehicleType,
|
||||
bool engineSound,
|
||||
bool toneOut
|
||||
);
|
||||
|
||||
void setServoLimits(
|
||||
byte lim1L,
|
||||
byte lim1R,
|
||||
byte lim2L,
|
||||
byte lim2R,
|
||||
byte lim3L,
|
||||
byte lim3R,
|
||||
byte lim3Llow,
|
||||
byte lim3Rlow,
|
||||
byte lim4L,
|
||||
byte lim4R
|
||||
);
|
||||
|
||||
void setIndicators(
|
||||
bool escBrakeLights,
|
||||
bool tailLights,
|
||||
bool headLights,
|
||||
bool indicators,
|
||||
bool beacons
|
||||
);
|
||||
|
||||
void setMotorConfig(
|
||||
int maxPWMfull,
|
||||
int maxPWMlimited,
|
||||
int minPWM,
|
||||
byte maxAccelerationFull,
|
||||
byte maxAccelerationLimited,
|
||||
float tiltCalibration,
|
||||
byte steeringTorque,
|
||||
byte pwmPrescaler2
|
||||
);
|
||||
|
||||
void setAdditionalChannels(
|
||||
bool TXO_momentary1,
|
||||
bool TXO_toggle1,
|
||||
bool potentiometer1
|
||||
);
|
||||
|
||||
bool getliPo(){ return m_liPo; }
|
||||
float getcutoffVoltage(){ return m_cutoffVoltage;}
|
||||
float getboardVersion(){ return m_boardVersion;}
|
||||
bool getHP(){ return m_HP;}
|
||||
int getvehicleNumber(){ return m_vehicleNumber;}
|
||||
vehicletype_t getvehicleType(){ return m_vehicleType;}
|
||||
void setvehicleType(vehicletype_t vhc){m_vehicleType = vhc;}
|
||||
bool getescBrakeLights(){ return m_escBrakeLights;}
|
||||
bool gettailLights(){ return m_tailLights;}
|
||||
bool getheadLights(){ return m_headLights;}
|
||||
bool getindicators(){ return m_indicators;}
|
||||
bool getbeacons(){ return m_beacons;}
|
||||
byte getlim1L(){ return m_lim1L;}
|
||||
byte getlim1R(){ return m_lim2R;}
|
||||
byte getlim2L(){ return m_lim2L;}
|
||||
byte getlim2R(){ return m_lim2R;}
|
||||
byte getlim3L(){ return m_lim3L;}
|
||||
byte getlim3R(){ return m_lim3R;}
|
||||
byte getlim3Llow(){ return m_lim3Llow;}
|
||||
byte getlim3Rlow(){ return m_lim3Rlow;}
|
||||
byte getlim4L(){ return m_lim4L;}
|
||||
byte getlim4R(){ return m_lim4R;}
|
||||
int getmaxPWMfull(){ return m_maxPWMfull;}
|
||||
int getmaxPWMlimited(){ return m_maxPWMlimited;}
|
||||
int getminPWM(){ return m_minPWM;}
|
||||
byte getmaxAccelerationFull(){ return m_maxAccelerationFull;}
|
||||
byte getmaxAccelerationLimited(){ return m_maxAccelerationLimited;}
|
||||
float gettiltCalibration(){ return m_tiltCalibration;}
|
||||
byte getsteeringTorque(){ return m_steeringTorque;}
|
||||
byte getpwmPrescaler2(){ return m_pwmPrescaler2; }
|
||||
void setpwmPrescaler2(byte psc) { m_pwmPrescaler2 = psc;}
|
||||
bool getTXO_momentary1(){ return m_TXO_momentary1;}
|
||||
bool getTXO_toggle1(){ return m_TXO_toggle1;}
|
||||
bool getpotentiometer1(){ return m_potentiometer1; }
|
||||
bool getengineSound(){ return m_engineSound;}
|
||||
bool gettoneOut(){ return m_toneOut;}
|
||||
|
||||
}; //class vehicleConfig
|
||||
|
||||
vehicleConfig* getConfigptr( void );
|
||||
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user