Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed BNO055_fusion
main.cpp
- Committer:
- pmmccorkell
- Date:
- 2019-09-18
- Revision:
- 0:6e9da949a2a3
File content as of revision 0:6e9da949a2a3:
#include "mbed.h"
#include "BNO055.h"
Serial pc(USBTX, USBRX);
I2C i2c(p9,p10);
DigitalOut pwr_on(p30);
BNO055 imu(i2c,p26);
//DigitalOut led1(LED1);
//instantiate global ready to 0
uint32_t ready = 0x00000000;
BNO055_ID_INF_TypeDef bno055_id_inf;
BNO055_EULER_TypeDef euler_angles;
//BNO055_QUATERNION_TypeDef quaternion;
//BNO055_LIN_ACC_TypeDef linear_acc;
BNO055_GRAVITY_TypeDef gravity;
//BNO055_TEMPERATURE_TypeDef chip_temp;
// Instantiate motor driver PWM pins, duty cycle, and period
double stop_pw=.0015;
//double fwd_pw=.0018;
//double rev_pw=.0012;
PwmOut RH_motor(p23);
PwmOut Lfwd(p21); // left forward
PwmOut Lrev(p22); // left reverse
PwmOut Rfwd(p24); // right forward
PwmOut Rrev(p25); // right reverse
float dutycycle=0.40;
//Lazy... don't want to track these down everywhere
//Standard servo rate -> 50Hz
double servo_freq=50;
double servo_period=(1/servo_freq);
//25us per data sheet Blue Robotics BESC-R1
double esc_step=(25/1000000);
//400Hz update frequency per data sheet
int esc_freq=400;
double esc_time_ms=(1000/esc_freq);
//1.1-1.9ms pw per data sheet
double esc_range=.0004;
//natural #
double e=2.71828;
//Data function pulls data from BNO and sends over serial
//Accepts unsigned 32bit key word (k) that RasPi will use as trigger
uint16_t data(uint32_t k) {
//Instantiate status array of 6 32-bit words.
//First 16 bits of each 32-bit word are Identifiers for RasPi to correctly assign the trailing 16 bits of data.
uint32_t status[6]={0};
//word 0: Key
//Used to ensure Pi and Mbed are on same page.
status[0]=k;
//word 1: Status information.
//0xffff acts as prefix to identify Status for RasPi.
//Last 3 bits (from right) are current position (POS[0-7]). See BNO datasheet.
//4th bit (from right) is RH turn motors enabled.
//5th bit (from right) is LH turn motors enabled.
status[1]=ready;
//word 2: Calibration.
//0xc000 acts as prefix to identify Cal for RasPi.
status[2]=0xc0000000+imu.read_calib_status();
//Get Euler data from BNO.
imu.get_Euler_Angles(&euler_angles);
//word 3 is Heading.
//0xc100 acts as prefix to identify Heading for RasPi.
uint16_t h = euler_angles.h;
status[3]=0xc1000000+h;
//Offset calculation: 360*16bit resolution = 5760 -> converted to hex = 0x1680
int offset=0x1680;
//word 4 is Roll.
//0xc300 acts as prefix to identify Roll for RasPi.
//BNO sends Roll and Pitch as +/- 180deg. Add offset of 360deg to avoid dealing with signed ints over serial.
uint16_t r = offset + euler_angles.r;
status[4]=0xc3000000+r;
//word 5 is Pitch.
//0xc500 acts as prefix to identify Pitch for RasPi.
//BNO sends Roll and Pitch as +/- 180deg. Add offset of 360deg to avoid dealing with signed ints over serial.
uint16_t p = offset + euler_angles.p;
status[5]=0xc5000000+p;
//For loop iterates through Status array to transmit 6 32-bit words over Serial with "\n" appended for Python in RasPi.
int i;
int l=(sizeof(status)/sizeof(uint32_t))-1;
for (i=0; i<=l; i++) {
pc.printf("%x\n", status[i]);
}
//return heading
return h;
}
//Function to setup motor PWM for right turn
void TurnRight() {
//mask off other bits and set Rmotor status to on
ready=(ready & 0xfffffff7) + 0x8;
// pc.printf("Turning Right\r\n");
Rfwd.write(dutycycle);
Lrev.write(dutycycle);
}
//Function to setup motor PWM for left turn
void TurnLeft() {
//mask off other bits and set Lmotor status to on
ready=(ready & 0xffffffef) + 0x10;
// pc.printf("Turning Left\r\n");
Lfwd.write(dutycycle);
Rrev.write(dutycycle);
}
//Function to stop all motors
void Stop_motors() {
//Set PW on all PWM pins to 0
Lfwd.write(0);
Lrev.write(0);
Rfwd.write(0);
Rrev.write(0);
//zero out Lm and Rm status bits
ready=(ready & 0xffffffe7);
}
//The sigmoid curve for logistic function.
//Used for continuous PWM and softstart / softstop near end points.
double Sigmoid(double Ymax, double m, double x0, double x) {
// double Logistic_func=(Ymax/(1+(e^((-1*m)*(x-x0)))));
double exponent=((-1*m)*(x-x0));
double power=pow(e,exponent);
double Logistic_func=Ymax/(1+power);
// pc.printf("exp: %f, power:%f, ret: %f\r\n",exponent,power,Logistic_func);
return Logistic_func;
}
//Function to control RH motor
void RH_motor_cntrl(double target_pw) {
double step_pw=.00005;
double current_pw=(RH_motor.read()*.02);
double diff_pw=abs(target_pw-current_pw);
//Time we want to take to get from 1.5 to [1.9 or 1.1]
// double Delay=0.5; //500ms
//Total # of steps / resolution of ESC
// double Steps=((.0019-.0015)/esc_step);
//Center of sigmoid curve
double Center=.1; //100ms -> ramp up within 200ms
//slope at Center
double Slope=50; //rate of ramp up
double time_count=0; //total time in while loop
double dwell_time=0.001; //time to wait between while loop
double tolerance_pw=.000045; //45us
double dir=0;
while(diff_pw>tolerance_pw) {
current_pw=(RH_motor.read()*.02);
diff_pw=abs(target_pw-current_pw);
if ((target_pw-current_pw)>=0) {
dir=(1);
//RH moving fwd
// ready=(ready & 0xfffffff7) + 0x8;
}
if ((target_pw-current_pw)<0) {
dir=(-1);
//RH moving rev
// ready=(ready & 0xffffffef) + 0x10;
}
double factor=Sigmoid(1,Slope,Center,time_count);
//double new_pw=stop_pw+(diff_pw*factor);
double new_pw;
double speed_pw=step_pw+(diff_pw*factor);
if (dir==1) new_pw=current_pw+speed_pw;
//new_pw=current_pw+.0000025;
if (dir==-1) new_pw=current_pw-speed_pw;
//new_pw=current_pw-.0000025;
pc.printf("diff: %f, factor: %f\r\n",diff_pw,factor);
pc.printf("pw: %f, speed: %f, offset: %f\r\n",current_pw,speed_pw,step_pw);
RH_motor.pulsewidth(new_pw);
wait_ms(dwell_time);
time_count+=dwell_time;
}
RH_motor.pulsewidth(target_pw);
//zero out Lm and Rm status bits
// ready=(ready & 0xffffffe7);
}
//Function to handle motor logic
void motors(int desired_heading) {
//Pull heading from BNO and correct for 16bit/deg resolution.
double current_heading=((data(0x1234abcd))/0x10);
//Set acceptable tolerance in deg, for heading accuracy.
double tolerance = 3.0;
//The natural #
//Error handling. Only continue for 0-360 range.
if (desired_heading>=0 and desired_heading<=360) {
//Calculate how far to turn in degrees.
double diff = abs(desired_heading-(current_heading));
//Correct for 360-0 edge cases if 'diff'erence is greater than 180.
//Change direction and recalculate for accurate 'tolerance' comparison.
if (diff>180) {
if (desired_heading>180) diff=((current_heading+180)-(desired_heading-180));
if (current_heading>180) diff=((desired_heading+180)-(current_heading-180));
}
while (diff > tolerance) {
double dir=1;
double speed=0;
current_heading=((data(0x1234abcd))/0x10);
diff=abs((desired_heading)-(current_heading));
//Sigmoid data
double center=15; //deg
double slope=0.5; //rate of change
if (diff>180) {
dir=(-1);
if (desired_heading>180) diff=((current_heading+180)-(desired_heading-180));
if (current_heading>180) diff=((desired_heading+180)-(current_heading-180));
}
if ((desired_heading-current_heading)<0) dir=(dir*-1);
speed=Sigmoid(esc_range,slope,center,diff);
if (dir==1) {
RH_motor_cntrl(stop_pw+speed);
TurnRight();
}
if (dir==-1) {
RH_motor_cntrl(stop_pw-speed);
TurnLeft();
}
wait_ms(20);
}
// pc.printf("heading: %i, diff: %i\r\n",current_heading,diff);
}
}
void maintain(int desired_heading) {
while(1) {
motors(desired_heading);
wait_ms(20);
}
}
//Function to accept desired position, program BNO,
// and update ready word by masking non-position bits
// and adding the new position to position bits
void switch_pos(int position) {
if (position>=0 and position<8) {
switch (position) {
case 1:
imu.set_mounting_position(MT_P1);
ready=((ready & 0xfffffff8)+0x1);
break;
case 2:
imu.set_mounting_position(MT_P2);
ready=((ready & 0xfffffff8)+0x2);
break;
case 3:
imu.set_mounting_position(MT_P3);
ready=((ready & 0xfffffff8)+0x3);
break;
case 4:
imu.set_mounting_position(MT_P4);
ready=((ready & 0xfffffff8)+0x4);
break;
case 5:
imu.set_mounting_position(MT_P5);
ready=((ready & 0xfffffff8)+0x5);
break;
case 6:
imu.set_mounting_position(MT_P6);
ready=((ready & 0xfffffff8)+0x6);
break;
case 7:
imu.set_mounting_position(MT_P7);
ready=((ready & 0xfffffff8)+0x7);
break;
case 0:
default:
imu.set_mounting_position(MT_P0);
ready=((ready & 0xfffffff8));
break;
}
}
}
//Commands function handles Serial input, checks for correct syntax, and calls appropriate function(s) to execute commands.
void commands() {
//Instantiate input char array to buffer Serial input.
char input[1000];
//check_pos char array used to filter Position commands.
char check_pos[5]="pos:";
//check_hea char array used to filter Go to Heading commands.
char check_hea[5]="hea:";
//check_mnt char array used to filter Maintain Heading commands.
char check_mnt[5]="mnt:";
//While loop reads Serial input into input buffer.
int i=0;
while (pc.readable()) {
input[i]=pc.getc();
// pc.printf("read char %c to %i\r\n",input[i], i);
i++;
}
//Only continue if input buffer has 7 entries, otherwise ignore input buffer.
//All commands from RasPi shall come in a format of 7 characters "abc:xyz"
// where 'abc' is an identifying string and 'xyz' is a decimal number.
if (i==7) {
// pc.printf("checking 0-%c, 1-%c, 2-%c, 3-%c, 4-%c \r\n",check_pos[0],check_pos[1],check_pos[2],check_pos[3],check_pos[4]);
//Instantiate counters that will be used to verify known commands.
int verified_pos=0;
int verified_hea=0;
int verified_mnt=0;
//While loop checks first 4 characters of input buffer and attempts to match
// against known commands.
int q=0;
while (q<4) {
//Increment verified_pos if a match is found between Serial input buffer
// and Position command format.
if (input[q]==check_pos[q]) {
verified_pos++;
// pc.printf("verified for pos %c at %i\r\n",input[q],q);
}
//Increment verified_hea if a match is found between Serial input buffer
// and Heading command format.
if (input[q]==check_hea[q]) {
// pc.printf("verified for hea %c at %i\r\n",input[q],q);
verified_hea++;
}
if (input[q]==check_mnt[q]) {
verified_mnt++;
}
q++;
}
//If first 4 characters from Serial input buffer match Position command format,
// execute "switch_pos" function.
if (verified_pos==4) {
int change=(input[6]-'0');
switch_pos(change);
}
//If first 4 characters from Serial input buffer match Heading command format,
// execute "motors" function.
if (verified_hea==4) {
//Correct for ascii '0', and reform 3digit decimal number
int hea100=(input[4]-'0');
int hea10=(input[5]-'0');
int hea1=(input[6]-'0');
int hea=(hea100*100)+(hea10*10)+(hea1*1);
// pc.printf("heading: %i\r\n",hea);
motors(hea);
//zero out ready status for LH and RH motors
ready=(ready & 0xffffffe7);
Stop_motors();
RH_motor_cntrl(stop_pw);
}
if (verified_mnt==4) {
int mnt100=(input[4]-'0');
int mnt10=(input[5]-'0');
int mnt1=(input[6]-'0');
int mnt=(mnt100*100)+(mnt10*10)+(mnt1*1);
maintain(mnt);
RH_motor_cntrl(stop_pw);
//zero out ready status for LH and RH motors
ready=(ready & 0xffffffe7);
}
}
}
int main() {
//engage plaidspeed
pc.baud(115200);
// imu.set_mounting_position(MT_P3);
RH_motor.period(.02);
RH_motor.pulsewidth(stop_pw);
//If not ready, reset BNO.
while (imu.chip_ready() == 0) {
do {
pc.printf("resetting BNO\r\n");
pwr_on=0;
wait_ms(100);
pwr_on=1;
wait_ms(50);
} while(imu.reset());
}
wait_ms(20);
//If BNO is ready, set ready status indication
if (imu.chip_ready()) {
ready=0xffff0000;
}
// uint32_t keyver=intake(ready);
//Look for serial input commands and send to 'commands' function.
//If no serial input commands, stream data.
while(1) {
if (pc.readable()) commands();
else data(0x1234abcd);
wait_ms(20);
}
}