Final Blog – TaoWang

Final Blog – Robot State Machine and Debugging Reflection


In the final stage of this project, my main work was focused on understanding and implementing the control logic of the robot using a state machine approach. The robot used distance sensor inputs through the ADC channels, and these sensor readings were then used to decide how the two motors should move. The basic idea was that the robot should be able to search for a target, detect when the target is present, move towards it, and correct its direction if the target is more clearly detected on the left or right side.

The final program was based on a PIC16F877A microcontroller running with CCS RTOS. The system was divided into several tasks. One task was responsible for reading the ADC values from the distance sensors. Two output tasks were responsible for generating PWM signals for the two motors. Another task worked as the state machine, which decided the next movement of the robot according to the sensor condition. This structure made the program easier to understand because the input reading, decision making, and motor control were separated into different parts.


// States used in the robot state machine
#define START       0
#define RADAR       1
#define CHASE       2
#define CHASE_LEFT  3
#define CHASE_RIGHT 4

// Inputs sent to the state machine
#define BOX_GONE   0
#define BOX        1
#define BOX_LEFT   2
#define BOX_RIGHT  3

// State machine variables
unsigned int8 state_machine_input1 = 0;
unsigned int8 state = START;

The following code shows the main states and input messages used by the state machine. These definitions are the basis of the state diagram shown below.




Figure 1 : State Machine Diagram of the Robot Control Logic

Figure 1 shows my understanding of the state machine used in the project. The first state is `START`. This is the initial state of the system. In this state, the program gives initial PWM values to the two motors and then moves into the `RADAR` state. I understand this as the preparation stage of the robot. Before the robot can chase anything, the system needs to start correctly, set the required variables, and make sure that the motor output tasks can receive PWM values.
The main transition logic in the program is shown below:
if(state == START)
{
   adc_value1 = 140;
   adc_value2 = 160;

   rtos_msg_send(output2_rtos_task, adc_value1);
   rtos_msg_send(output1_rtos_task, adc_value2);

   lcd_gotoxy(1,4);
   printf(lcd_putc,"RADAR");

   state = RADAR;
}

else if((state == RADAR) && state_machine_input1 == BOX)
{
   adc_value1 = 100;
   adc_value2 = 100;

   rtos_msg_send(output2_rtos_task, adc_value1);
   rtos_msg_send(output1_rtos_task, adc_value2);

   lcd_putc('\f');
   lcd_gotoxy(1,4);
   printf(lcd_putc,"CHASE");

   state = CHASE;
}

else if((state == CHASE) && (state_machine_input1 == BOX_LEFT))
{
   adc_value1 = 140;
   adc_value2 = 160;

   rtos_msg_send(output2_rtos_task, adc_value1);
   rtos_msg_send(output1_rtos_task, adc_value2);

   lcd_putc('\f');
   lcd_gotoxy(1,4);
   printf(lcd_putc,"CHASE LEFT");

   state = CHASE_LEFT;
}

else if((state == CHASE) && (state_machine_input1 == BOX_RIGHT))
{
   adc_value1 = 160;
   adc_value2 = 140;

   rtos_msg_send(output2_rtos_task, adc_value1);
   rtos_msg_send(output1_rtos_task, adc_value2);

   lcd_putc('\f');
   lcd_gotoxy(1,4);
   printf(lcd_putc,"CHASE RIGHT");

   state = CHASE_RIGHT;
}

else if((state == CHASE_LEFT) && (state_machine_input1 == BOX))
{
   adc_value1 = 100;
   adc_value2 = 100;

   rtos_msg_send(output2_rtos_task, adc_value1);
   rtos_msg_send(output1_rtos_task, adc_value2);

   lcd_putc('\f');
   lcd_gotoxy(1,4);
   printf(lcd_putc,"CHASE (L)");

   state = CHASE;
}

else if((state == CHASE_RIGHT) && (state_machine_input1 == BOX))
{
   adc_value1 = 100;
   adc_value2 = 100;

   rtos_msg_send(output2_rtos_task, adc_value1);
   rtos_msg_send(output1_rtos_task, adc_value2);

   lcd_putc('\f');
   lcd_gotoxy(1,4);
   printf(lcd_putc,"CHASE (R)");

   state = CHASE;
}





Part2
This part  below of the program reads the two ADC channels and converts the sensor values into simple state machine messages, such as BOX, BOX_GONE, BOX_LEFT and BOX_RIGHT.

set_adc_channel(1);
delay_ms(1);
adc_value1 = read_adc();

if(adc_value1 > 120)
   adc_value1 = 120;

if(adc_value1 < 20)
   adc_value1 = 20;

set_adc_channel(2);
delay_ms(1);
adc_value2 = read_adc();

if(adc_value2 > 120)
   adc_value2 = 120;

if(adc_value2 < 20)
   adc_value2 = 20;

if (adc_value1 <= 20 && adc_value2 <= 20)
{
   rtos_msg_send(state_machine_rtos_task, BOX_GONE);
}
else if (adc_value1 > 20 && adc_value2 > 20)
{
   rtos_msg_send(state_machine_rtos_task, BOX);
}
else if (adc_value1 > adc_value2 + 20)
{
   rtos_msg_send(state_machine_rtos_task, BOX_LEFT);
}
else if (adc_value2 > adc_value1 + 20)
{
   rtos_msg_send(state_machine_rtos_task, BOX_RIGHT);
}

After the start-up stage, the robot enters the `RADAR` state. In my understanding, `RADAR` means that the robot is searching or scanning for the object. The robot has not yet confirmed that the object is in front of it, so the control logic should keep checking the ADC values from the sensors. In the code, the input task reads two ADC channels and limits the values to a safe range. Then it compares the two sensor readings and sends a message to the state machine. If both sensor readings are very low, the program treats this as `BOX_GONE`, which means no target is detected. If both readings are above the threshold, it sends `BOX`, which means the target is detected.

When the state machine is in `RADAR` and receives the `BOX` message, it changes to the `CHASE` state. This is the main action state of the robot. In this state, the robot assumes that the object is in front of it and drives both motors forward. In the code, this is done by sending PWM-related values to the two output tasks. The output tasks then multiply the received values and use them to generate pulse widths for the motor control pins. My understanding is that the state machine does not directly drive the motors itself. Instead, it decides the behaviour and sends commands to the motor output tasks. This is a useful design because the decision logic and hardware output logic are kept separate.

The `CHASE` state can move to either `CHASE_LEFT` or `CHASE_RIGHT`. If the left sensor value is significantly larger than the right sensor value, the input is treated as `BOX_LEFT`. This means the target is more on the left side, so the robot should adjust its movement to re-centre the target. The state machine then enters `CHASE_LEFT`. Similarly, if the right sensor value is significantly larger than the left sensor value, the input becomes `BOX_RIGHT`, and the robot moves into `CHASE_RIGHT`. In these two states, the PWM values sent to the two motors are different. This makes one motor move differently from the other, so that the robot can turn slightly and correct its direction.

The arrows returning from `CHASE_LEFT` and `CHASE_RIGHT` back to `CHASE` are also important. When the two sensor readings become balanced again and the object is detected by both sensors, the state machine receives `BOX`. This means the target is now more centred, so the robot can leave the correction state and go back to the normal chasing state. This is how the state machine creates a simple feedback behaviour. The robot does not need a very complicated algorithm. It only needs to compare the sensor readings and choose the correct state.

Another important transition is `BOX_GONE`. If the target disappears, the state machine should not keep chasing blindly. In the diagram, `BOX_GONE` sends the system back to `RADAR`. This means the robot returns to searching mode when the object is no longer detected. This part is important because it prevents the robot from staying in a wrong chasing state when the sensor input no longer supports that behaviour.

From this project, I also learned that implementing the code is only one part of the work. A large amount of time was spent on debugging the actual circuit and hardware connections. During the project, we had problems where some node voltages were not correct. At first, it was not clear whether the problem came from the code, the wiring, the sensor, or the motor controller. We used the oscilloscope to check different nodes and compare the measured voltage with the expected signal. This helped us narrow down the possible problem area step by step.

Another useful skill I practised was reading the layout and finding the correct pins. Sometimes the connection on the board was not obvious from the top side, so we had to look at the layout diagram and also turn the board over to trace the physical node. This process trained my ability to connect the schematic, the layout, and the real hardware together. I found that this is very different from only reading code on a computer. In a real embedded system, the software, the microcontroller pins, the sensor outputs, the motor controller, and the power connections all need to match correctly.

My personal understanding is that the state machine is the high-level “brain” of the robot, while the ADC input task and PWM output tasks are like the sensing and action parts. The ADC readings tell the program what the robot is seeing, the state machine decides what the robot should do next, and the PWM output tasks translate that decision into motor movement. This separation made the whole system clearer and easier to debug.

The biggest lesson from this project is that engineering work is a continuous process of finding problems and solving them. Even when the program seems logically correct, the hardware may still behave differently because of incorrect node voltage, wrong connection, wrong pin mapping, or signal timing problems. Using the oscilloscope, checking the layout, and tracing the board manually helped me understand how engineers approach real problems. This project improved not only my coding ability, but also my practical debugging ability and my understanding of how embedded software and hardware work together.


I would also like to sincerely thank Jason for giving us the opportunity to experience this kind of team-based engineering work. This was not only a coding or hardware project, but also a valuable teamwork experience. During the project, we had to discuss problems together, divide the work, test different possible solutions, and help each other understand both the software and hardware parts of the system. This made me realise that engineering projects are rarely completed by one person alone. In a real company environment, engineers need to communicate clearly, share responsibilities, debug problems together, and learn from each other’s experience. Therefore, I believe this teamwork experience is very valuable for us, especially as we prepare to enter industry in the future.

Comments

Popular posts from this blog

Blog 1 - Igor Kapusniak

Blog 5 - Bin Bot Project - Davin Barron