S21: (RC)^2

From Embedded Systems Learning Academy
Revision as of 16:19, 28 May 2021 by Proj user10 (talk | contribs) (Master Module)

Jump to: navigation, search

(RC)2

(RC)2 <Add Pictures of it here!>



Abstract

Team (RC)² built an RC car capable of navigating to a destination controlled by an Android application while avoiding obstacles that may be in its path. The RC car is composed of various nodes that each have a specific task such as reading sensor data, controlling the motor, and attaining compass values. These individual nodes communicate and share pertinent information regarding the car’s operation with each other over a CAN bus.

Introduction

The project was divided into five main components that were important to the RC car as a whole:

  • Sensor Node
  • Motor Node
  • Geographic Controller
  • Communication Bridge/OLED
  • Driver Node

Team Members & Responsibilities

<Team Picture>

Gitlab Project Link - https://gitlab.com/rc-2/sjtwo-c



Schedule

Week# Start Date End Date Task Status
1 03/01/2021 03/07/2021
  • Read previous projects, gather information and discuss among the group members.
  • Distribute modules to each team member.
Complete
2 03/08/2021 03/14/2021
  • Host individual group meetings and review past semester projects for parts to purchase
  • Generate individual meeting notes to set baselines for project team meeting
  • Update to-buy list excel sheet with any parts ordered
  • Set up GitLab for team project
Complete
3 03/15/2021 03/21/2021
  • Setup GitLab for Android Application
  • Design mockups for the Android Application.
  • Interface SJ2 board and the ESP8266:Connecting to a local Wireless Access point
  • Try adding the MQTT Client library and publish and subscribe the first message to/from the server
  • Get distance data from ultrasonic sensors
  • Send ultrasonic sensor data over CAN bus
  • Control RC car motor speed and wheel alignment with SJ2 board
Complete
4 03/22/2021 03/28/2021
  • Solder GPS & compass modules
  • Motor: Control Motor node with Ultrasonic sensor data
  • Master Driver: Basic rules for driving the car
  • Code review before Spring Break
Complete
5 03/29/2021 04/04/2021
  • Geographical: Get compass and GPS data and send over CAN bus
  • Android Application Prototype[1]
  • Master Driver: Add Rules for heading to destination based on GPS/Compass value
  • Set up prototype board for all nodes
  • DBC file review for current system
Complete
6 04/05/2021 04/11/2021
  • Power requirements for PCB design
  • Submit pin-outs for all connected peripherals
  • Integrate ESP-8266 with SJ2 (read from Wi-Fi module)
  • Integrate Google Maps API into Android Application
  • Start on PCB Design
  • Interface OLED for data display
  • Test Run 1: Sensor and Motor Node integration
Complete
7 04/12/2021 04/18/2021
  • Establish MQTT broker on a cloud
  • Interface with SJ2 using Android Mobile phone (Send and acknowledge coordinates)
  • Finalize Geo node, test for its accuracy
  • Define and test for "ideal" running environment for all nodes, focus on Sensor and Geo node
  • Acceleration/Velocity calculation with using acceleration sensor
  • Send PCB design to a manufacturer
  • Test Run 2: Sensor/Motor/Geo car driving
  • DBC file finalize
Complete
8 04/19/2021 04/25/2021
  • Feature improvement and bug fixes for Android Application and MQTT protocol
  • Test and verify PCB board
  • Power Delivery for the project
  • Master Driver: Add Rule for driving control based on current speed
  • Test Run 3: Find any potential bugs and improvement in the ideal running environment
Complete
9 04/26/2021 05/02/2021
  • Update the DBC for any final changes
  • Test Run 4: Improvement from Test Run 3
  • Update Wiki Report
Complete
10 05/03/2021 05/09/2021
  • Outer casing for (RC)²
  • Test Run 5: Improvement from Test Run 4
Complete
11 05/10/2021 05/16/2021
  • Test Run 6: Improvement from Test Run 5
Complete
12 05/17/2021 05/23/2021
  • Check for operational bugs
  • Perform final bug fixes for any/all nodes
  • Video for (RC)²
  • Test Run 7: Improvement from Test Run 6
Complete
13 05/24/2021 05/26/2021
  • Finalize Wiki Report
  • Finalize code and commit to master branch
  • Complete Final Demo
Complete


Parts List & Cost

TODO: Check Drive and add to this table

Item# Part Desciption Vendor Qty Cost
1 RC Car Traxxas 1 $129.99
2 CAN Transceivers MCP2551-I/P Microchip [2] 8 Free Samples
3 GPS Module Adafruit [3] 1 $59.99
4 Compass Adafruit [4] 1 $14.95
5 ESP8266 (WiFi) DIYmall [5] 1 $14.95
6 HC-020K (Wheel Encoder) Hilitchi [6] 1 $8.99
7 URM09 Ultrasonic Sensor (I²C) DFRobot [7] 4 $12.90
8 SJ2 Board Microcontroller SCE [8] 5 $250.00
9 Male/Female Headers DEPEPE [9] 1 $5.00
10 Screw Terminal Block Connectors DZGGI [10] 1 $7.99
11 CR1220 Lithium Battery Energizer [11] 1 $5.90


Printed Circuit Board


Pcb design rcrc 1.jpeg Pcb design rcrc 2.jpeg Pcb design rcrc 3.jpeg Pcb design rcrc 4.jpeg



CAN Communication

Priority Logic:

  • Our two highest priority signals are DRIVE_STOP and DRIVE_START, and we prioritized stopping the car over starting the car as a matter of safety.
  • GPS_DESTINATION_LOCATION is the next highest signal because we would need to have a valid location to be able to properly navigate the car.
  • SONAR_VALUES has a priority right below GPS_DESTINATION_LOCATION because once the destination is set, the car would need to read sensor data to avoid potential obstacles.
  • COMPASS_HEADING_DISTANCE would be the signal with a priority below the SONAR_VALUES signal as the car would need a proper heading in order to navigate to a destination.
  • MOTOR_VALUE is the signal sending from Driver Node to the Motor Node to actuate the car
  • The signals with the lowest priority are those that we used in our debugging process over Busmaster.


Missing-In-Action Management:

  • Driver: stop the car when missing the Sensor information when the frame is missing for more than 500ms
  • Motor: straighten the car out and stop the car
  • Geo: <insert version>


Hardware Design

Our initial CAN bus was based on a breadboard that contained a lot of jumper/dupont wires. This design was sufficient for testing purposes as we worked to meet the in-class checkpoints; however, as we developed our RC car, we realized that a handful of connections were troublesome to the development of the car. As opposed to continuously rewiring and checking connections, we decided to invest time into PCB design to minimize connections and fix hardware issues. Below is a before and after comparison of our RC Car.


CAN without PCB.jpeg
CAN with pcb.jpeg


DBC File

Current DBC File

VERSION ""

NS_ :
	BA_
	BA_DEF_
	BA_DEF_DEF_
	BA_DEF_DEF_REL_
	BA_DEF_REL_
	BA_DEF_SGTYPE_
	BA_REL_
	BA_SGTYPE_
	BO_TX_BU_
	BU_BO_REL_
	BU_EV_REL_
	BU_SG_REL_
	CAT_
	CAT_DEF_
	CM_
	ENVVAR_DATA_
	EV_DATA_
	FILTER
	NS_DESC_
	SGTYPE_
	SGTYPE_VAL_
	SG_MUL_VAL_
	SIGTYPE_VALTYPE_
	SIG_GROUP_
	SIG_TYPE_REF_
	SIG_VALTYPE_
	VAL_
	VAL_TABLE_

BS_:

BU_: DBG DRIVER IO MOTOR SENSOR GEO BRIDGE


BO_ 101 DRIVE_STOP: 1 BRIDGE
 SG_ DRIVE_STOP : 0|8@1+ (1,0) [0|0] "" DRIVER

BO_ 102 DRIVE_START: 1 BRIDGE
 SG_ DRIVE_START : 0|8@1+ (1,0) [0|0] "" DRIVER

BO_ 121 GPS_DESTINATION_LOCATION: 8 BRIDGE
 SG_ DEST_LATITUDE : 0|28@1+ (0.000001,-90.000000) [-90|90] "deg" GEO
 SG_ DEST_LONGITUDE : 28|29@1+ (0.000001,-180.000000) [-180|180] "deg" GEO

BO_ 131 SONAR_VALUES: 8 SENSOR 
 SG_ SONAR_VALUES_left : 0|8@1+ (1,0) [0|150] "cm" DRIVER 
 SG_ SONAR_VALUES_right : 8|8@1+ (1,0) [0|150] "cm" DRIVER 
 SG_ SONAR_VALUES_front : 16|8@1+ (1,0) [0|150] "cm" DRIVER 
 SG_ SONAR_VALUES_back : 24|8@1+ (1,0) [0|150] "cm" DRIVER

BO_ 141 COMPASS_HEADING_DISTANCE: 8 GEO
  SG_ HEADING : 0|12@1+ (0.1,0) [0|359.9] "deg" DRIVER,BRIDGE
  SG_ BEARING : 12|12@1+ (0.1,0) [0|359.9] "deg" DRIVER,BRIDGE
  SG_ DISTANCE : 24|17@1+ (0.01,0) [0|0] "m" DRIVER,BRIDGE 

BO_ 151 MOTOR_VALUES: 3 DRIVER
 SG_ MOTOR_VALUES_speed : 0|8@1+ (1,-25) [-25|25] "kph" MOTOR
 SG_ MOTOR_VALUES_steering : 8|6@1+ (0.1,-2.1) [-2.1|2.1] "deg" MOTOR

BO_ 161 MOTOR_VALUES_TO_DRIVER: 3 MOTOR
 SG_ MOTOR_VALUES_TO_DRIVER_speed : 0|8@1+ (1,-25) [-25|25] "kph" DRIVER

BO_ 171 GPS_DESTINATION_REACHED: 1 GEO
 SG_ DESTINATION_REACHED : 0|1@1+ (1,0) [0|1] "bool" DRIVER,BRIDGE

BO_ 181 GPS_RAW_DATA: 8 GEO
 SG_ CURR_LATITUDE : 0|28@1+ (0.000001,-90.000000) [-90|90] "deg" BRIDGE
 SG_ CURR_LONGITUDE : 28|29@1+ (0.000001,-180.000000) [-180|180] "deg" BRIDGE
 SG_ GPS_QUALITY : 57|2@1+ (1,0) [0|2] "val" BRIDGE

BO_ 182 COMPASS_ACCEL_RAW_DATA: 6 GEO
 SG_ ACCEL_X : 0|16@1+ (0.01,-162.01) [-162.01|162.01] "ms^2" BRIDGE
 SG_ ACCEL_Y : 16|16@1+ (0.01,-162.01) [-162.01|162.01] "ms^2" BRIDGE
 SG_ ACCEL_Z : 32|16@1+ (0.01,-162.01) [-162.01|162.01] "ms^2" BRIDGE

BO_ 183 COMPASS_MAG_RAW_DATA: 6 GEO
 SG_ MAG_X : 0|16@1+ (0.01,-162.01) [-162.01|162.01] "mT" BRIDGE
 SG_ MAG_Y : 16|16@1+ (0.01,-162.01) [-162.01|162.01] "mT" BRIDGE
 SG_ MAG_Z : 32|16@1+ (0.01,-162.01) [-162.01|162.01] "mT" BRIDGE

BO_ 191 DRIVER_HEARTBEAT: 1 DRIVER
 SG_ DRIVER_HEARTBEAT_cmd : 0|8@1+ (1,0) [0|0] "" SENSOR, MOTOR, GEO

CM_ BU_ DRIVER "The driver controller driving the car";
CM_ BU_ MOTOR "The motor controller of the car";
CM_ BU_ SENSOR "The sensor controller of the car";
CM_ BO_ 191 "Sync message used to synchronize the controllers";
CM_ SG_ 191 DRIVER_HEARTBEAT_cmd "Heartbeat command from the driver";

BA_DEF_ "BusType" STRING ;
BA_DEF_ BO_ "GenMsgCycleTime" INT 0 0;
BA_DEF_ SG_ "FieldType" STRING ;

BA_DEF_DEF_ "BusType" "CAN";
BA_DEF_DEF_ "FieldType" "";
BA_DEF_DEF_ "GenMsgCycleTime" 0;

BA_ "GenMsgCycleTime" BO_ 100 1000;
BA_ "GenMsgCycleTime" BO_ 200 50;
BA_ "FieldType" SG_ 100 DRIVER_HEARTBEAT_cmd "DRIVER_HEARTBEAT_cmd";

VAL_ 100 DRIVER_HEARTBEAT_cmd 2 "DRIVER_HEARTBEAT_cmd_REBOOT" 1 "DRIVER_HEARTBEAT_cmd_SYNC" 0 "DRIVER_HEARTBEAT_cmd_NOOP" ;



Sensor ECU

Sensor Node GitLab Link

The sensor node is responsible for gathering information from the 4 surrounding sensors. Our team decided to keep the sensor node by itself as avoiding obstacles is more critical than other tasks in our design. Thus, keeping an ECU free of any other task and only responsible for gathering sensor data and push it onto the CAN bus is our solution for a worry-free sensor reading

How the Sensors are connected to the SJ2 Board

Hardware Design

1. CAN Transceiver

This module will be used to communicate with the Driver Node to send data sensor values through CAN communication protocol. The CAN transceiver will be powered by a +3.3V source with its Transmit Line (Tx) connected to GPIO Pin P0.1 and its Receive Line (Rx) connected to GPIO Pin P0.0.

2. Ultrasonic Sensor

Getting the right distance requires some research on each team depends on your project requirement. Our team settle for a no-frill Ultrasonic Sensor from DFRobots due to its easy implementation and its cost-effective. As Preet always mentioned in class: Think like an engineering being paid $100/hour, don't waste your time on cheap part that can take you way too long to implement. This sensor from DFRobots have different communication protocol that your team can choose based on your own experience. We want to focus on other node so we decided to choose the I2C package that makes configuring and reading value sensor from this node simple.
align

Software Design

The Sensor Node consists of 3 main modules: can_bus_message_handler, ultrasonic_system, and ultrasonic_sensor

1. The ultrasonic_sensor module This module is written to provide the user with access to our sensor. I simplified the driver to provide users with only 3 main functions:

  • 'void ultrasonic_sensor__init_i2c_port(i2c_e i2c_number)'
    Initialize the i2c port that is used for the sensors
  • 'void ultrasonic_sensor__enable_burst_mode(i2c_e i2c_number, uint8_t sensor_address)'
    Enable burst measuring mode for our sensor
  • 'uint8_t ultrasonic_sensor__get_value(i2c_e i2c_number, uint8_t sensor_address)'
    Get the sensor reading provides the sensor address

2. The ultrasonic_system module This module is built on top of the ultrasonic_sensor module above so that it can be implemented with the whole system. It provides the user with 2 main functions:

  • 'void ultrasonic_system__init(void)'
    This function is responsible for initiating the 4 ultrasonic sensors with their addresses and configure the i2c communication
  • 'dbc_SONAR_VALUES_s ultrasonic_system__get_all_values(void)'
    This function is responsible for getting all the ultrasonic sensors, storing them in a local sensor struct created by the auto-generated code from the DBC file, and return that struct back to the user

3. The can_bus_message_handler module This module is designed to be simple. Thus, the only function needs in this case is:

  • 'bool can_bus_message_handler__transmit_sensor(void)'
    This function is responsible for calling the ultrasonic_system__get_all_values as discussed above and send the data over the CAN bus

Technical Challenges

1. Bad ultrasonic sensor

Although there are several economical ultrasonic sensors out there, we think it is not worth the time debugging an ultrasonic sensor that may fail at any given time. Our initial ultrasonic sensor gives us decent readings but it occasionally fails to read the distance. We did some research on other sensors and found the one from DFRobots which is easy to implement and provide a decent reading with our range of application.

1. Integration with Bridge node

When we order our PCB, we decided to combine the Bridge node and the Ultrasonic node together. However, our test shows that the structure of our programs is not in sync, which made the ultrasonic sensor froze for some time during its operation. We decided to separate the two nodes. Should the 2 nodes are developed with the same structures at the beginning, we could potentially get it working. Nonetheless, these Nodes are designed differently hence integrating it requires a complete rewrite of the nodes which we decided not to pursue


Motor ECU

Motor Node GitLab Link

Connection of all four Motor modules to SJ2 Board

Hardware Design


The hardware design for the motor node requires four main components to be connected to the SJ2 Board:

1. CAN Transceiver

This module will be used to communicate with the Driver Node to receive the desired speed and steering angle through CAN communication protocol. This module will also be used to transmit the car's current speed to the Driver Node. The CAN transceiver will be powered by a +3.3V source with its Transmit Line (Tx) connected to GPIO Pin P0.1 and its Receive Line (Rx) connected to GPIO Pin P0.0.
RC Transmit Controller


2. RC Transmit Controller

Due to the Electronic Speed Controller (ESC) being completely resined into the RC Car's base, there was no direct method of controlling the provided ESC with the SJ2 Board. The team decided to control the ESC using the transmit controller by measuring the internal potentiometer's voltage signal line to the transmit circuit and replicating those voltage values with a Digital-to-Analog Converter (DAC). This transmit controller takes in two input lines to its ports:
  • CON2 PORT
This port takes in one input, which controls the steering angle of the car. The voltage value for the steering to be straight was measured at +2.0V. Lower voltage values steer the car left and higher voltage values steer the car right. The voltage input to this port comes from the External DAC.
  • CON3 PORT
This port takes in one input, which controls the speed of the car. The voltage value for the car to be stationary was measured at approximately +1.5V. Lower voltage values accelerated the car forward and higher voltage values accelerated the car backwards. The voltage input to this port comes from the SJ2 Board's DAC pin P0.26.


External DAC

3. External DAC

Since the SJ2 Board has only one DAC pin, which is used to control the motor speed, an additional external DAC is needed to control the car's servo motor for steering.



HC-020K Wheel Encoder



4. Wheel Encoder

This module is used by the motor node to measure the car's speed by keeping track of how many revolutions the rotary revolves around the sensor. The rotary contains twenty hole slits, so a full revolution will "tick" the sensor twenty times.







Software Design

The motor node is designed to receive a desired speed and a desired steering angle from the driver node. These values will be used to adjust the RC Car's motor speed and servo motor angle to align with those requested values.

There are four public functions that are provided in the motor node.

1. void motors__init(void)

This function is used by the periodic callback to initialize the required peripherals used to control the motor speed as well as the servo motor angle.

2. void motors__run(void)

This function is used by the periodic callback to set the motor speed and the servo motor angle. This public function will invoke two private functions, motors__speed_handler() which handles the motor speed of the car, and motors__steering_handler() which handles the steering angle of the car.
  • motors__speed_handler()
This private function controls the RC Car's speed, which is calculated by measuring the circumference of the car's wheel and finding out how many times it rotates within a given time frame. Setting the time frame and obtaining the circumference of the wheel is trivial, however, obtaining the amount of rotations required additional effort. We used an HC-020K Wheel Encoder to measure how many times the wheel rotated around its axle and connected the +5V output to one of the SJ2's GPIO pins. This pin was then attached to an interrupt service, which incremented the static variable holding the number of times the wheel has rotated. The sample code to calculate the speed of the car is shown below.
    static int8_t motors__private_get_speed_kmh(void) {
      int8_t motor_speed = current_speed_kmh;
      uint64_t current_time_ms = sys_time__get_uptime_ms();
      uint64_t time_elapsed_ms = current_time_ms - wheel_encoder_previous_time_ms;

      if (time_elapsed_ms >= 100U) {
        wheel_encoder_previous_time_ms = current_time_ms;

        if (time_elapsed_ms > 0) {
          float number_of_wheel_cycles = (float)wheel_encoder_number_of_ticks / (float)WHEEL_ENCODER_TICKS_PER_CYCLE;
          float distance_travelled_cm = (float)(number_of_wheel_cycles * WHEEL_CIRCUMFERENCE_CM);

          motor_speed =
              (distance_travelled_cm / CENTIMETERS_IN_ONE_KILOMETER) / ((float)time_elapsed_ms / MILLISECONDS_IN_ONE_HOUR);

          wheel_encoder_number_of_ticks = 0U;

          if (MOTORS__DIRECTION_REVERSE == current_motor_direction) {
            motor_speed *= -1;
          }

        }
      }

    return motor_speed;
  }
  • motors__steering_handler()
This private function controls the RC Car's steering angle.
The steering driver works in a similar way to how the motor driver works in the sense that it makes use of a digital to analog converter (DAC) to send signals to the motor transceiver to tell the car how much to steer. First, the motor node receives a CAN message through the CAN bus that contains a float value from within the range of -2.1 (full lock to the left) to 2.1 (full lock to the right). The steering driver then converts this float to an integer number used to write to the 12-bit DAC through I2C communication. Essentially, the range [-2.1, 2.1] which has 42 steps, is mapped to the range of [0, 4096] using the equation: (input + 2.1) * 10 * 4096 / 42.

3. void motors__set_speed_and_steering_values(dbc_MOTOR_VALUES_s *motor_values)

This function is used by the CAN Bus Message Handler to set the desired speed and steering angle. When the CAN Bus Message Handler module receives a CAN package, it will decode the motor values and call this function. This function is also used by the MIA handler as well to stop the RC Car if it doesn't receive any CAN messages from the driver node ten times in a 10Hz periodic callback.

4. dbc_MOTOR_VALUES_TO_DRIVER_s motors__get_motor_values(void)

This function is used by the CAN Bus Message Handler to transmit the current speed of the car.


Technical Challenges


  • Electronic Speed Controller (ESC)
When opening the RC car, the team found that the ESC was fully resined into the car's base. This prevented the team from directly controlling the ESC, so the team decided to control the RC car's speed and steering through the RC car's remote transceiver. The remote transceiver was taken apart and the team discovered that the user controlled the speed and steering through the use of two potentiometers, which sent specific voltage values to the transceiver's circuit and sent those values to the ESC remotely via Bluetooth. The team developed an internal DAC driver and an external DAC driver to send controlled voltage values to the transceiver to simulate the potentiometer output values.
  • Wheel Encoder Issues
During the integration of the wheel encoder, our team encountered various issues regarding its positioning on the car and its debouncing circuitry.
Due to the shape of the RC car's plastic base, it was difficult to find a position on the car to place the wheel encoder such that it could accurately read the amount of revolutions the rotary wheel has made. The encoder must be placed near the axle of the wheel, however, there was no platform large enough to fix the encoder onto. The team purchased a metal "L" bracket and fixated the bracket onto one of the base's thin plastic platforms. This bracket was used as a new platform to mount the wheel encoder onto for better positioning and reading accuracy.
Additionally, mounting the rotary wheel onto the axle was another issue. Initially, the rotary wheel had to be broken apart and crudely super-glued together. However, this solution resulted in a less accurate encoder reading due to the rotary being warped from the initial break. The team 3D printed a new rotary wheel in two separate pieces to resolve the warping issue from breaking it ourselves. This resulted in a more consistent, accurate encoder reading.
  • Motor startup sequence
Initially, when starting up the car, the team faced an issue where although voltage values are being sent to accelerate the car, the car would remain stationary. The solution to this issue was found in the user manual. According to the RC Car's user manual, both the acceleration and the steering triggers must be neutral when powering on the car. This means that when starting up, both the motor and the servo motor must be set to specific voltage values. The team measured the potentiometer's voltage value outputs when the triggers were in a neutral position and used those voltage values when starting up.


Geographical Controller

<Picture and link to Gitlab>

Hardware Design

Software Design

<List the code modules that are being called periodically.>

Technical Challenges

< List of problems and their detailed resolutions>

Challenge: One of the largest challenges we had with the geo_node was processing the heading value to send over the CAN bus. This was an issue because the heading value would change drastically when the car itself would shake, and the car shakes quite a lot. This resulted in occasional sporadic heading values causing the car to behave chaotically.


Solution: The best solution to the heading resulting in sporadic values was to take an average of the heading values and use a certain threshold to determine whether or not to throw out a processed heading value. This solution of taking an average and using a threshold created a problem of going from 360 to 0 as the difference between these would never be within the threshold. We solved the issue by mapping our raw degree values to their sine values to allow the values to fit within the threshold.




Communication Bridge Controller & OLED

<Picture and link to Gitlab>

Hardware Design

Software Design

<List the code modules that are being called periodically.>

Technical Challenges

< List of problems and their detailed resolutions>



Driver Module

Driver Node GitLab Link The Driver node is responsible for receiving data from the Sensor Node, Geo Node, and Bridge Node to issue appropriate commands to the Motor Node

Hardware Design

Software Design

<List the code modules that are being called periodically.>

Technical Challenges

< List of problems and their detailed resolutions>



Mobile Application

<Picture and link to Gitlab>

Hardware Design

Software Design

<List the code modules that are being called periodically.>

Technical Challenges

< List of problems and their detailed resolutions>






Conclusion

Conclusion <Organized summary of the project>


To conclude, (RC)^2 was a success and we are proud of the RC car. <write more>

<What did you learn?>

  • CAN Communication Protocol
  • Code modularization
  • DBC Files
  • Unit Test
  • Debugging
  • Mobile Application Design

<write more>


Project Video

Project Source Code

Advice for Future Students

<Bullet points and discussion>

Acknowledgement

  • Arun's roommate?

=== References ===