Whenever i use hc-05 Bluetooth module with software serial my timer 3 interrupt doesn't work correctly and this interrupt controls stepper motor so it's essential to me that it works in the right intervals (every 25 micro seconds) is there a way to give higher priority to the interrupt controlling the steppers, i don't care if the communication or main void loop takes more time
Ps i already have pcb so i can't really use hardware serial communication
There's the error. You are clearly still in process of developing your circuit & code. Why would you make a PCB when you don't yet know what the circuit will be?
Is that even an option? You forgot to tell the forum what model of Arduino you are using.
I am using arduino nano, in the testing phase for some reason the hardware serial wasn't working could be loose jumpers or maybe the breadboard idk. I didn't want to use hardware serial anyway because i would have to disconnect the bt module each time i upload a code so i thought software serial would be better option.
I was thinking of shorting pins 8 and 9 (the ones i am using for software serial) to Rx and Tx in the pcb but i don't know if that could damage the arduino
Thing is i only need to receive data for a couple minutes or so just once then i will be sending data (one command every while so it doesn't really affect the steppers)
On Nano 3 (atmega328), you don't have the option of hardware serial, there is only one hardware serial port and it is used for uploading your code and using serial monitor.
Could you use a hardware timer to generate the pulses for the stepper? This would run independently of the code running and would not be affected by software serial.
looked in couple stores but couldn't find it + honestly i don't want to spend any more money on this project especially since i need to collect these data only once, i was thinking of connecting both my arduino uno and nano to the sensor (mpu6050) so one of them transmits the sensor readings and the other takes care of the stepping.
this is a smaller version the code since the main one needs gyro sensor
#include <SoftwareSerial.h>
const byte rxPin = 9;
const byte txPin = 8;
SoftwareSerial BTSerial(rxPin, txPin); // RX TX
int ie,error,plot,pos;
float delta;
float ready=0;
float rec=0;
byte start, received_byte;
int receive_counter;
unsigned long loop_timer;
//Various settings
float pid_p_gain = 5 ; //Gain setting for the P-controller (15)
float pid_i_gain = 0.4; //Gain setting for the I-controller (1.5)
float pid_d_gain = 0.3; //Gain setting for the D-controller (30)
float turning_speed = 20; //Turning speed (20)
float max_target_speed = 150; //Max target speed (100)
int left_motor, throttle_left_motor, throttle_counter_left_motor, throttle_left_motor_memory;
int right_motor, throttle_right_motor, throttle_counter_right_motor, throttle_right_motor_memory;
float angle_gyro, angle_acc, angle, self_balance_pid_setpoint;
float pid_error_temp, pid_i_mem, pid_setpoint, gyro_input, pid_output, pid_last_d_error;
float pid_output_left, pid_output_right;
void setup() {
// put your setup code here, to run once:
pinMode(rxPin, INPUT);
pinMode(txPin, OUTPUT);
pinMode(2, OUTPUT);
pinMode(3, OUTPUT); //Configure digital poort 2 as output
pinMode(4, OUTPUT); //Configure digital poort 3 as output
pinMode(5, OUTPUT); //Configure digital poort 4 as output
pinMode(7, OUTPUT);
pinMode(13, OUTPUT);
BTSerial.begin(38400);
//To create a variable pulse for controlling the stepper motors a timer is created that will execute a piece of code (subroutine) every 20us
//This subroutine is called TIMER2_COMPA_vect
TCCR2A = 0; //Make sure that the TCCR2A register is set to zero
TCCR2B = 0; //Make sure that the TCCR2A register is set to zero
TIMSK2 |= (1 << OCIE2A); //Set the interupt enable bit OCIE2A in the TIMSK2 register
TCCR2B |= (1 << CS21); //Set the CS21 bit in the TCCRB register to set the prescaler to 8
OCR2A = 24; //The compare register is set to 39 => 20us / (1s / (16.000.000MHz / 8)) - 1
TCCR2A |= (1 << WGM21); //Set counter 2 to CTC (clear timer on compare) mode
loop_timer = micros() + 50000;
}
void loop() {
// put your main code here, to run repeatedly:
if(BTSerial.available()){
ready=rec;
rec = BTSerial.read();
if((rec==248)||(rec==249)||(rec==250)||(rec==251))
receive_counter=25;
if (ready!=rec){
if(ready==255){
pid_p_gain = rec;
rec=0;
ready=0;
}
if(ready==254){
pid_i_gain = rec/10;
rec=0;
ready=0;
}
if(ready==253){
pid_d_gain=rec/10;
rec=0;
ready=0;
}
}
}
if(receive_counter == 1)rec=0;
if(receive_counter < 0)receive_counter --; //The received byte will be valid for 25 program loops (100 milliseconds)
pid_error_temp = angle_gyro - self_balance_pid_setpoint - pid_setpoint;
if(pid_output > 10 || pid_output < -10)pid_error_temp += pid_output * 0.015 ;
pid_i_mem += pid_i_gain * pid_error_temp; //Calculate the I-controller value and add it to the pid_i_mem variable
if(pid_i_mem > 400)pid_i_mem = 400; //Limit the I-controller to the maximum controller output
else if(pid_i_mem < -400)pid_i_mem = -400;
//Calculate the PID output value
pid_output = pid_p_gain * pid_error_temp + pid_i_mem + pid_d_gain * delta;
if(pid_output > 400)pid_output = 400; //Limit the PI-controller to the maximum controller output
else if(pid_output < -400)pid_output = -400;
pid_last_d_error = pid_error_temp; //Store the error for the next loop
if(pid_output < 5 && pid_output > -5)pid_output = 0; //Create a dead-band to stop the motors when the robot is balanced
if(angle_gyro > 50 || angle_gyro < -50 || start == 0 ){ //If the robot tips over or the start variable is zero or the battery is empty
pid_output = 0; //Set the PID controller output to 0 so the motors stop moving
pid_i_mem = 0; //Reset the I-controller memory
start = 0; //Set the start variable to 0
self_balance_pid_setpoint = 0; //Reset the self_balance_pid_setpoint variable
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Control calculations
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
pid_output_left = pid_output; //Copy the controller output to the pid_output_left variable for the left motor
pid_output_right = pid_output; //Copy the controller output to the pid_output_right variable for the right motor
if(rec==248){ //If the first bit of the receive byte is set change the left and right variable to turn the robot to the left
pid_output_left += turning_speed; //Increase the left motor speed
pid_output_right -= turning_speed; //Decrease the right motor speed
}
if(rec==249){ //If the second bit of the receive byte is set change the left and right variable to turn the robot to the right
pid_output_left -= turning_speed; //Decrease the left motor speed
pid_output_right += turning_speed; //Increase the right motor speed
}
if(rec==251){ //If the third bit of the receive byte is set change the left and right variable to turn the robot to the right
if(pid_setpoint > -2.5)pid_setpoint -= 0.05; //Slowly change the setpoint angle so the robot starts leaning forewards
if(pid_output > max_target_speed * -1)pid_setpoint -= 0.005; //Slowly change the setpoint angle so the robot starts leaning forewards
}
if(rec==250){ //If the forth bit of the receive byte is set change the left and right variable to turn the robot to the right
if(pid_setpoint < 2.5)pid_setpoint += 0.05; //Slowly change the setpoint angle so the robot starts leaning backwards
if(pid_output < max_target_speed)pid_setpoint += 0.005; //Slowly change the setpoint angle so the robot starts leaning backwards
}
if(!(rec==251 || rec==250)){ //Slowly reduce the setpoint to zero if no foreward or backward command is given
if(pid_setpoint > 0.5)pid_setpoint -=0.05; //If the PID setpoint is larger then 0.5 reduce the setpoint with 0.05 every loop
else if(pid_setpoint < -0.5)pid_setpoint +=0.05; //If the PID setpoint is smaller then -0.5 increase the setpoint with 0.05 every loop
else pid_setpoint = 0; //If the PID setpoint is smaller then 0.5 or larger then -0.5 set the setpoint to 0
}
//The self balancing point is adjusted when there is not forward or backwards movement from the transmitter. This way the robot will always find it's balancing point
if(pid_setpoint == 0){ //If the setpoint is zero degrees
if(pid_output < 0)self_balance_pid_setpoint += 0.0015; //Increase the self_balance_pid_setpoint if the robot is still moving forewards
if(pid_output > 0)self_balance_pid_setpoint -= 0.0015; //Decrease the self_balance_pid_setpoint if the robot is still moving backwards
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Motor pulse calculations
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//To compensate for the non-linear behaviour of the stepper motors the folowing calculations are needed to get a linear speed behaviour.
if(pid_output_left > 0)pid_output_left = 405 - (1/(pid_output_left + 9)) * 5500;
else if(pid_output_left < 0)pid_output_left = -405 - (1/(pid_output_left - 9)) * 5500;
if(pid_output_right > 0)pid_output_right = 405 - (1/(pid_output_right + 9)) * 5500;
else if(pid_output_right < 0)pid_output_right = -405 - (1/(pid_output_right - 9)) * 5500;
//Calculate the needed pulse time for the left and right stepper motor controllers
if(pid_output_left > 0)left_motor = 400 - pid_output_left;
else if(pid_output_left < 0)left_motor = -400 - pid_output_left;
else left_motor = 0;
if(pid_output_right > 0)right_motor = 400 - pid_output_right;
else if(pid_output_right < 0)right_motor = -400 - pid_output_right;
else right_motor = 0;
if(start == 0 && angle_gyro > -5 && angle_gyro < 5){ //If the accelerometer angle is almost 0
start = 1; //Set the start variable to start the PID controller
}
//Copy the pulse time to the throttle variables so the interrupt subroutine can use them
throttle_left_motor = left_motor;
throttle_right_motor = right_motor;
/*
Serial.print(angle_gyro);
Serial.print(" , ");
Serial.print(delta);
Serial.print(" , ");
Serial.println(pid_output);
*/
while((loop_timer-8000) > micros());
if (rec==252)
plot=1;
if(rec==0){
plot=0;
pid_setpoint=0;
pid_output_left = pid_output;
pid_output_right = pid_output;
}
if(plot==1)
BTSerial.print(angle_gyro);
PORTB |= 0b00100000;
if(micros() <= loop_timer)
{
PORTB &= 0b11011111;
}
if(PINB & 0b00100000){
while(PINB & 0b00100000){
throttle_left_motor = 0;
throttle_right_motor = 0;}
}
while(loop_timer > micros());
loop_timer=loop_timer+15000;
}
ISR(TIMER2_COMPA_vect){
//Left motor pulse calculations
throttle_counter_left_motor ++; //Increase the throttle_counter_left_motor variable by 1 every time this routine is executed
if(throttle_counter_left_motor > throttle_left_motor_memory){ //If the number of loops is larger then the throttle_left_motor_memory variable
throttle_counter_left_motor = 0; //Reset the throttle_counter_left_motor variable
throttle_left_motor_memory = throttle_left_motor; //Load the next throttle_left_motor variable
if(throttle_left_motor_memory < 0){ //If the throttle_left_motor_memory is negative
PORTD &= 0b11111011; //Set output 5 low to reverse the direction of the stepper controller
throttle_left_motor_memory *= -1; //Invert the throttle_left_motor_memory variable
}
else PORTD |= 0b00000100; //Set output 5 high for a forward direction of the stepper motor
}
else if(throttle_counter_left_motor == 1)PORTD |= 0b00001000; //Set output 6 high to create a pulse for the stepper controller
else if(throttle_counter_left_motor == 2)PORTD &= 0b11110111; //Set output 6 low because the pulse only has to last for 20us
//right motor pulse calculations
throttle_counter_right_motor ++; //Increase the throttle_counter_right_motor variable by 1 every time the routine is executed
if(throttle_counter_right_motor > throttle_right_motor_memory){ //If the number of loops is larger then the throttle_right_motor_memory variable
throttle_counter_right_motor = 0; //Reset the throttle_counter_right_motor variable
throttle_right_motor_memory = throttle_right_motor; //Load the next throttle_right_motor variable
if(throttle_right_motor_memory < 0){ //If the throttle_right_motor_memory is negative
PORTD &= 0b11011111; //Set output 3 low to reverse the direction of the stepper controller
throttle_right_motor_memory *= -1; //Invert the throttle_right_motor_memory variable
}
else PORTD |= 0b00100000; //Set output 3 high for a forward direction of the stepper motor
}
else if(throttle_counter_right_motor == 1)PORTD |= 0b10000000; //Set output 4 high to create a pulse for the stepper controller
else if(throttle_counter_right_motor == 2)PORTD &= 0b01111111; //Set output 4 low because the pulse only has to last for 20us
}
Share a schematic diagram for your system + a good photo of the finished PCB (both sides). Also upload your code here, or at least (preferably) a minimal version that demonstrates the problem.
Without this information it's virtually useless trying to offer meaningful advice.
Edit: thanks for adding the code. I have to ask - was this generated (at least in part) with AI (e.g. chatGPT)?
Thank you for the codes. As your code lines are too many to follow, what I can do for you is to receive commands from Android Phone via BT and change the positions of a SG-90 Servo Motor.
From your code, I understand that at every 25 us interval, you are controlling the Motor on interrupt basis. I would like if you briefly explain how the TC2 is controlling your Motor. Also, give a link to your Motor.
One potential problem is the failure to declare pin 6 as an OUTPUT.
It appears the Timer2 ISR assumes that the motors are driven on pins 3 & 4 (right motor) and 5 & 6 (left motor). If you don't declare pin 6 as an output, the left motor will not function.
The rest of the ISR may/should still run. You can verify this by lighting a LED from the ISR, for instance. You can use the onboard LED on your Arduino for this purpose.
Like I hinted at above, trying to debug this if you don't understand what the code does is going to be a painful and frustrating exercise. It sounds like you copied some stuff from GitHUB (perhaps the PCB as well?) and hoped that it would be a "plug & play" affair, but embedded systems engineering tends to be a little more involved. If you want to learn, then buckle up because it's going to be a ride - but it can be fun.
i am using stepper motors not servo , i am not entirely sure how it works as i said in another reply to rsmls i got most of the code from github , all i know is that when the value of pid changes the speed of the motors change.
my theory is each timer interrupt it either turns the pin high or if high it turns it low and some times it does nothing based on the speed required (given by pid value).
One more thing; assuming you built your own hardware, but got the code off of GitHUB, you need to realize that some aspects of the hardware behavior are actually hardcoded into this program. Particularly there is a section that corrects for non-linearities that you may/will have to revise/adjust to the specifics of your hardware.
This is some kind of self-balancing robot, I assume?
yes its a self balancing robot , i designed the pcb and was using a code i wrote but it wasn't really working so i got this code and adjusted it for my pins connections and it does work perfectly as long as i don't send data to my phone once i do the wheels becomes much slower and it falls if pushed hard, i am using same nema17 steppers so i don't know about the non linear part but it does work so why bother ,
thing is i know what the problem is simply sending data using software serial interrupts the code and prevents timer2 interrupts from working . i guess i will have to buy arduino nano bt
thanks anyway
also here is the schematic if you re interested
I can see that the pulses are generated in a way which uses a hardware timer, but also an interrupt function. The timer triggers the interrupt function and the interrupt function generates the pulses. This is why you get the problem, as you already know, because software serial is blocking the interrupt function sometimes. The interrupt function is executing every 25us which is 40,000 times per second. I am kind of surprised it works at all!
The hardware timers can generate the pulses directly, without interrupt function. This way they would be unaffected by software serial. But the hardware timer will continue to generate the same pulses until it's parameters are updated, so the logic for that will still need to be run many times per second to enable the robot to balance. But that can be maybe only 100 to 1,000 times per second instead of 40,000 times per second, and it would not need to be in an interrupt function.
This change would require some complex coding. If you are at the stage of copying code from GitHub, it may be beyond your level of experience at the moment.